home *** CD-ROM | disk | FTP | other *** search
/ Skunkware 98 / Skunkware 98.iso / src / mail / pine3.96.tar.gz / pine3.96.tar / pine3.96 / pine / mailindx.c < prev    next >
C/C++ Source or Header  |  1996-07-11  |  114KB  |  4,351 lines

  1. #if !defined(lint) && !defined(DOS)
  2. static char rcsid[] = "$Id: mailindx.c,v 4.224 1996/07/11 22:47:41 mikes Exp $";
  3. #endif
  4. /*----------------------------------------------------------------------
  5.  
  6.             T H E    P I N E    M A I L   S Y S T E M
  7.  
  8.    Laurence Lundblade and Mike Seibel
  9.    Networks and Distributed Computing
  10.    Computing and Communications
  11.    University of Washington
  12.    Administration Builiding, AG-44
  13.    Seattle, Washington, 98195, USA
  14.    Internet: lgl@CAC.Washington.EDU
  15.              mikes@CAC.Washington.EDU
  16.  
  17.    Please address all bugs and comments to "pine-bugs@cac.washington.edu"
  18.  
  19.  
  20.    Pine and Pico are registered trademarks of the University of Washington.
  21.    No commercial use of these trademarks may be made without prior written
  22.    permission of the University of Washington.
  23.  
  24.    Pine, Pico, and Pilot software and its included text are Copyright
  25.    1989-1996 by the University of Washington.
  26.  
  27.    The full text of our legal notices is contained in the file called
  28.    CPYRIGHT, included with this distribution.
  29.  
  30.  
  31.    USENET News reading additions in part by L Lundblade / NorthWestNet, 1993
  32.    lgl@nwnet.net
  33.  
  34.    Pine is in part based on The Elm Mail System:
  35.     ***********************************************************************
  36.     *  The Elm Mail System  -  Revision: 2.13                             *
  37.     *                                                                     *
  38.     *             Copyright (c) 1986, 1987 Dave Taylor              *
  39.     *             Copyright (c) 1988, 1989 USENET Community Trust   *
  40.     ***********************************************************************
  41.  
  42.  
  43.   ----------------------------------------------------------------------*/
  44.  
  45. /*======================================================================
  46.     mailindx.c
  47.     Implements the mail index screen
  48.      - most code here builds the header list and displays it
  49.  
  50.  ====*/
  51.  
  52. #include "headers.h"
  53.  
  54.  
  55. static struct key index_keys[] = 
  56.        {{"?","Help",KS_SCREENHELP},    {"O","OTHER CMDS",KS_NONE},
  57.     {"M","Main Menu",KS_MAINMENU},    {"V","[ViewMsg]",KS_VIEW},
  58.     {"P","PrevMsg",KS_PREVMSG},    {"N","NextMsg",KS_NEXTMSG},
  59.     {"-","PrevPage",KS_PREVPAGE},    {"Spc","NextPage",KS_NEXTPAGE},
  60.     {"D","Delete",KS_DELETE},    {"U","Undelete",KS_UNDELETE},
  61.     {"R","Reply",KS_REPLY},        {"F","Forward",KS_FORWARD},
  62.  
  63.     {"?","Help",KS_SCREENHELP},    {"O","OTHER CMDS",KS_NONE},
  64.     {"Q","Quit",KS_EXIT},        {"C","Compose",KS_COMPOSER},
  65.     {"L","ListFldrs",KS_FLDRLIST},    {"G","GotoFldr",KS_GOTOFLDR},
  66.     {"Tab","NextNew",KS_NONE},    {"W","WhereIs",KS_WHEREIS},
  67.     {"Y","prYnt",KS_PRINT},        {"T","TakeAddr",KS_TAKEADDR},
  68.     {"S","Save",KS_SAVE},        {"E","Export",KS_EXPORT},
  69.  
  70.     {"?","Help",KS_SCREENHELP},    {"O","OTHER CMDS",KS_NONE},
  71.     {"X",NULL,KS_NONE},        {"&","unXclude",KS_NONE},
  72.     {";","Select",KS_SELECT},    {"A","Apply",KS_APPLY},
  73.     {"$","SortIndex",KS_SORT},    {"J","Jump",KS_JUMPTOMSG},
  74.     {"H","HdrMode",KS_HDRMODE},    {"B","Bounce",KS_BOUNCE},
  75.     {"*","Flag",KS_FLAG},        {"|","Pipe",KS_NONE},
  76.  
  77.     {"?","Help",KS_SCREENHELP},    {"O","OTHER CMDS",KS_NONE},
  78.     {":","SelectCur",KS_SELECTCUR},    {"Z","ZoomMode",KS_NONE},
  79.     {NULL,NULL,KS_NONE},        {NULL,NULL,KS_NONE},
  80.     {NULL,NULL,KS_NONE},        {NULL,NULL,KS_NONE},
  81.     {NULL,NULL,KS_NONE},        {NULL,NULL,KS_NONE},
  82.     {NULL,NULL,KS_NONE},        {NULL,NULL,KS_NONE}};
  83. INST_KEY_MENU(index_keymenu, index_keys);
  84. #define PREVM_KEY 4
  85. #define NEXTM_KEY 5
  86. #define EXCLUDE_KEY 26
  87. #define UNEXCLUDE_KEY 27
  88. #define SELECT_KEY 28
  89. #define APPLY_KEY 29
  90. #define VIEW_FULL_HEADERS_KEY 32
  91. #define BOUNCE_KEY 33
  92. #define FLAG_KEY 34
  93. #define VIEW_PIPE_KEY 35
  94. #define ZOOM_KEY 39
  95.  
  96. static struct key nr_anon_index_keys[] = 
  97.        {{"?","Help",KS_SCREENHELP},    {"W","WhereIs",KS_WHEREIS},
  98.     {"Q","Quit",KS_EXIT},        {"V","[ViewMsg]",KS_VIEW},
  99.     {"P","PrevMsg",KS_PREVMSG},    {"N","NextMsg",KS_NEXTMSG},
  100.     {"-","PrevPage",KS_PREVPAGE},    {"Spc","NextPage",KS_NEXTPAGE},
  101.     {"F","Fwd Email",KS_FORWARD},    {"J","Jump",KS_JUMPTOMSG},
  102.     {"$","SortIndex",KS_SORT},    {NULL,NULL,KS_NONE}};
  103. INST_KEY_MENU(nr_anon_index_keymenu, nr_anon_index_keys);
  104.  
  105. static struct key nr_index_keys[] = 
  106.        {{"?","Help",KS_SCREENHELP},    {"O","OTHER CMDS",KS_NONE},
  107.     {"Q","Quit",KS_EXIT},        {"V","[ViewMsg]",KS_VIEW},
  108.     {"P","PrevMsg",KS_PREVMSG},    {"N","NextMsg",KS_NEXTMSG},
  109.     {"-","PrevPage",KS_PREVPAGE},    {"Spc","NextPage",KS_NEXTPAGE},
  110.     {"F","Fwd Email",KS_FORWARD},    {"J","Jump",KS_JUMPTOMSG},
  111.     {"Y","prYnt",KS_PRINT},        {"S","Save",KS_SAVE},
  112.  
  113.     {"?","Help",KS_SCREENHELP},    {"O","OTHER CMDS",KS_NONE},
  114.     {"E","Export",KS_EXPORT},    {"C","Compose",KS_COMPOSER},
  115.     {"$","SortIndex",KS_SORT},    {NULL,NULL,KS_NONE},
  116.     {NULL,NULL,KS_NONE},        {"W","WhereIs",KS_WHEREIS},
  117.     {NULL,NULL,KS_NONE},        {NULL,NULL,KS_NONE},
  118.     {NULL,NULL,KS_NONE},        {NULL,NULL,KS_NONE}};
  119. INST_KEY_MENU(nr_index_keymenu, nr_index_keys);
  120.   
  121. static struct key simple_index_keys[] = 
  122.        {{"?","Help",KS_SCREENHELP},    {NULL,NULL,KS_NONE},
  123.     {"E","ExitSelect",KS_EXITMODE},    {"S","[Select]",KS_SELECT},
  124.     {"P","PrevMsg",KS_PREVMSG},    {"N","NextMsg",KS_NEXTMSG},
  125.     {"-","PrevPage",KS_PREVPAGE},    {"Spc","NextPage",KS_NEXTPAGE},
  126.     {"D","Delete",KS_DELETE},    {"U","Undelete",KS_UNDELETE},
  127.     {NULL,NULL,KS_NONE},        {NULL,NULL,KS_NONE}};
  128. INST_KEY_MENU(simple_index_keymenu, simple_index_keys);
  129.  
  130.  
  131. static OtherMenu what_keymenu = FirstMenu;
  132. jmp_buf jump_past_qsort;
  133.  
  134.  
  135. /*-----------
  136.   Saved state to redraw message index body 
  137.   ----*/
  138. struct entry_state {
  139.     unsigned hilite:1;
  140.     unsigned bolded:1;
  141.     long     id;
  142. };
  143.  
  144.  
  145. #define MAXIFLDS 20  /* max number of fields in index format */
  146. static struct index_state {
  147.     long        msg_at_top,
  148.             lines_per_page;
  149.     struct      entry_state *entry_state;
  150.     MSGNO_S    *msgmap;
  151.     MAILSTREAM *stream;
  152.     int         status_col;        /* column for select X's */
  153. } *current_index_state = NULL;
  154.  
  155. static int get_body_for_index;
  156.  
  157.  
  158. /*
  159.  * cache used by get_sub() so it doesn't have to do a fetchstructure
  160.  * and malloc/free for every qsort comparison...
  161.  */
  162. #if    defined(DOS) && !defined(_WINDOWS)
  163. #define    SUB_CACHE_LEN    128
  164. typedef struct {
  165.     short used;                /* whether entry's been used or not */
  166.     char  buf[SUB_CACHE_LEN];        /* actual subject string */
  167. } scache_ent;
  168.  
  169.  
  170. static struct scache {
  171.     short    last;            /* last one referenced */
  172.     long    size,            /* number of elements */
  173.         msgno[2];        /* which message in which entry */
  174.     scache_ent  ent[2];            /* subject and cache info for msg */
  175.     char       *cname;            /* file containing cache */
  176.     FILE       *cfile;
  177. } *scache = NULL;
  178. #else
  179. static struct scache {
  180.     long   size;
  181.     char **ent;
  182. } *scache = NULL;
  183. #endif
  184.  
  185.  
  186. /*
  187.  * Internal prototypes
  188.  */
  189. int     update_index PROTO((struct pine *, struct index_state *));
  190. int     index_scroll_up PROTO((long));
  191. int     index_scroll_down PROTO((long));
  192. int     index_scroll_to_pos PROTO((long));
  193. long     top_ent_calc PROTO((MAILSTREAM *, MSGNO_S *, long, long));
  194. char    *get_sub PROTO((long));
  195. int      compare_subjects PROTO((const QSType *, const QSType *));
  196. int      compare_subject_2 PROTO((const QSType *, const QSType *));
  197. int      compare_from PROTO((const QSType *, const QSType *));
  198. int      compare_to PROTO((const QSType *, const QSType *));
  199. int      compare_cc PROTO((const QSType *, const QSType *));
  200. int      compare_message_dates PROTO((const QSType *, const QSType *));
  201. int      compare_size PROTO((const QSType *, const QSType *));
  202. int      compare_arrival PROTO((const QSType *, const QSType *));
  203. HLINE_S *get_index_cache PROTO((long));
  204. int     set_index_addr PROTO((MAILSTREAM *, long, char *, ADDRESS *, char *,
  205.                    int, char *));
  206. int      i_cache_size PROTO((long));
  207. int      i_cache_width PROTO(());
  208. void     setup_header_widths PROTO((INDEX_COL_S *, int, long));
  209. int     parse_index_format PROTO((char *, INDEX_COL_S **));
  210. void     set_need_format_setup();
  211. void     clear_need_format_setup();
  212. int     check_need_format_setup();
  213. void     msgno_flush_selected PROTO((MSGNO_S *, long));
  214. void     second_subject_sort PROTO((long *, long, long));
  215. void     init_subject_cache PROTO((long));
  216. int     subject_cache_slot PROTO((long));
  217. char    *subject_cache_ent PROTO((long));
  218. char    *subject_cache_add PROTO((long, char *));
  219. void     clear_subject_cache PROTO(());
  220. #ifdef    DOS
  221. void     i_cache_hit PROTO((long));
  222. void     icread PROTO((void));
  223. void     icwrite PROTO((void));
  224. #ifdef    _WINDOWS
  225. int     index_scroll_callback PROTO((int,long));
  226. int     index_gettext_callback PROTO((char *, void **, long *, int *));
  227. #endif
  228. #endif
  229. long     seconds_since_epoch PROTO((long));
  230.  
  231.  
  232.  
  233.  
  234. /*----------------------------------------------------------------------
  235.  
  236.  
  237.   ----*/
  238. void
  239. do_index_border(cntxt, folder, stream, msgmap, style, which_keys, flags)
  240.      CONTEXT_S   *cntxt;
  241.      char        *folder;
  242.      MAILSTREAM  *stream;
  243.      MSGNO_S     *msgmap;
  244.      IndexType    style;
  245.      int         *which_keys, flags;
  246. {
  247.     if(flags & INDX_CLEAR)
  248.       ClearScreen();
  249.  
  250.     if(flags & INDX_HEADER)
  251.       set_titlebar((stream == ps_global->mail_stream)
  252.              ? (style == MsgIndex || style == MultiMsgIndex)
  253.                  ? "FOLDER INDEX"
  254.              : "ZOOMED FOLDER INDEX"
  255.              : (!strcmp(folder, INTERRUPTED_MAIL))
  256.              ? "COMPOSE: SELECT INTERRUPTED"
  257.              : "COMPOSE: SELECT POSTPONED",
  258.            stream, cntxt, folder, msgmap, 1, MessageNumber, 0, 0);
  259.  
  260.     if(flags & INDX_FOOTER) {
  261.     struct key_menu *km;
  262.     bitmap_t     bitmap;
  263.  
  264.     setbitmap(bitmap);
  265.     if(ps_global->anonymous)
  266.       km = &nr_anon_index_keymenu;
  267.     else if(ps_global->nr_mode)
  268.           km = &nr_index_keymenu;
  269.     else if(ps_global->mail_stream != stream)
  270.       km = &simple_index_keymenu;
  271.     else{
  272.         km = &index_keymenu;
  273.  
  274. #ifndef DOS
  275.         if(F_OFF(F_ENABLE_PIPE,ps_global))
  276. #endif
  277.           clrbitn(VIEW_PIPE_KEY, bitmap);  /* always clear for DOS */
  278.         if(F_OFF(F_ENABLE_FULL_HDR,ps_global))
  279.           clrbitn(VIEW_FULL_HEADERS_KEY, bitmap);
  280.         if(F_OFF(F_ENABLE_BOUNCE,ps_global))
  281.           clrbitn(BOUNCE_KEY, bitmap);
  282.         if(F_OFF(F_ENABLE_FLAG,ps_global))
  283.           clrbitn(FLAG_KEY, bitmap);
  284.         if(F_OFF(F_ENABLE_AGG_OPS,ps_global)){
  285.         clrbitn(SELECT_KEY, bitmap);
  286.         clrbitn(APPLY_KEY, bitmap);
  287.  
  288.         /*
  289.          * Since "Zoom" is alone on the last keymenu page
  290.          * if it's not enabled, hide that menu altogether
  291.          */
  292.         if(style != ZoomIndex){
  293.             clrbitn(ZOOM_KEY, bitmap);
  294.             km->how_many = 3;
  295.         }
  296.         else
  297.           km->how_many = 4;
  298.         }
  299.         else
  300.           km->how_many = 4;
  301.  
  302.         if(IS_NEWS(stream)){
  303.         index_keys[EXCLUDE_KEY].label = "eXclude";
  304.         KS_OSDATASET(&index_keys[EXCLUDE_KEY], KS_NONE);
  305.         }
  306.         else {
  307.         clrbitn(UNEXCLUDE_KEY, bitmap);
  308.         index_keys[EXCLUDE_KEY].label = "eXpunge";
  309.         KS_OSDATASET(&index_keys[EXCLUDE_KEY], KS_EXPUNGE);
  310.         }
  311.  
  312.         if(style == MultiMsgIndex){
  313.         clrbitn(PREVM_KEY, bitmap);
  314.         clrbitn(NEXTM_KEY, bitmap);
  315.         }
  316.     }
  317.         draw_keymenu(km, bitmap, ps_global->ttyo->screen_cols,
  318.                1-FOOTER_ROWS(ps_global), 0, what_keymenu, 0);
  319.     what_keymenu = SameTwelve;
  320.     if(which_keys)
  321.       *which_keys = km->which;  /* pass back to caller */
  322.     }
  323. }
  324.  
  325.       
  326.     
  327. /*----------------------------------------------------------------------
  328.         Main loop executing commands for the mail index screen
  329.  
  330.    Args: state -- the pine_state structure for next/prev screen pointers
  331.                   and to pass to the index manager...
  332.  ----*/
  333.  
  334. void
  335. mail_index_screen(state)
  336.      struct pine *state;
  337. {
  338.     dprint(1, (debugfile, "\n\n ---- MAIL INDEX ----\n"));
  339.     if(!state->mail_stream) {
  340.     q_status_message(SM_ORDER, 0, 3, "No folder is currently open");
  341.         state->prev_screen = mail_index_screen;
  342.     state->next_screen = main_menu_screen;
  343.     return;
  344.     }
  345.  
  346.     index_lister(state, state->context_current, state->cur_folder,
  347.          state->mail_stream, state->msgmap);
  348.     state->prev_screen = mail_index_screen;
  349. }
  350.  
  351.  
  352.  
  353. /*----------------------------------------------------------------------
  354.         Main loop executing commands for the mail index screen
  355.  
  356.    Args: state -- pine_state structure for display flags and such
  357.          msgmap -- c-client/pine message number mapping struct
  358.  ----*/
  359.  
  360. int
  361. index_lister(state, cntxt, folder, stream, msgmap)
  362.      struct pine *state;
  363.      CONTEXT_S   *cntxt;
  364.      char        *folder;
  365.      MAILSTREAM  *stream;
  366.      MSGNO_S     *msgmap;
  367. {
  368.     int         ch, orig_ch, which_keys, force, cur_row, cur_col, km_popped;
  369.     long     i, j, k, old_max_msgno;
  370.     IndexType    style, old_style = MsgIndex;
  371.     struct index_state id;
  372. #if defined(DOS) || defined(OS2)
  373.     extern void (*while_waiting)();
  374. #endif
  375.  
  376.     dprint(1, (debugfile, "\n\n ---- INDEX MANAGER ----\n"));
  377.     
  378.     ch                    = 'x';    /* For displaying msg 1st time thru */
  379.     force                 = 0;
  380.     km_popped             = 0;
  381.     state->mangled_screen = 1;
  382.     what_keymenu          = FirstMenu;
  383.     memset((void *)&id, 0, sizeof(struct index_state));
  384.     current_index_state   = &id;
  385.     id.msgmap    = msgmap;
  386.  
  387.     if((id.stream = stream) != state->mail_stream)
  388.       clear_index_cache();    /* BUG: should better tie stream to cache */
  389.  
  390.     set_need_format_setup();
  391.  
  392.     while (1) {
  393.     if(km_popped){
  394.         km_popped--;
  395.         if(km_popped == 0){
  396.         clearfooter(state);
  397.         if(!state->mangled_body
  398.            && id.entry_state
  399.            && id.lines_per_page > 1){
  400.             id.entry_state[id.lines_per_page-2].id = -1;
  401.             id.entry_state[id.lines_per_page-1].id = -1;
  402.         }
  403.         else
  404.           state->mangled_body = 1;
  405.         }
  406.     }
  407.  
  408.     old_max_msgno = mn_get_total(msgmap);
  409.  
  410.     /*------- Check for new mail -------*/
  411.         new_mail(force, NM_TIMING(ch), 1);
  412.     force = 0;            /* may not need to next time around */
  413.  
  414.     /*
  415.      * This is only actually necessary if this causes the width of the
  416.      * message number field to change.  That is, it depends on the
  417.      * format the user is using as well as on the max_msgno.  Since it
  418.      * should be rare, we'll just do it whenever it happens.
  419.      * Also have to check for a reduction in max_msgno when we expunge.
  420.      */
  421.     if(old_max_msgno < 1000L && mn_get_total(msgmap) >= 1000L
  422.        || old_max_msgno < 10000L && mn_get_total(msgmap) >= 10000L
  423.        || old_max_msgno < 100000L && mn_get_total(msgmap) >= 100000L){
  424.         clear_index_cache();
  425.         state->mangled_body = 1;
  426.         }
  427.  
  428.         if(streams_died())
  429.           state->mangled_header = 1;
  430.  
  431.         if(state->mangled_screen){
  432.             state->mangled_header = 1;
  433.             state->mangled_body   = 1;
  434.             state->mangled_footer = 1;
  435.             state->mangled_screen = 0;
  436.         }
  437.  
  438.     /*
  439.      * events may have occured that require us to shift from
  440.      * mode to another...
  441.      */
  442.     style = (any_lflagged(msgmap, MN_HIDE))
  443.           ? ZoomIndex
  444.           : (mn_total_cur(msgmap) > 1L) ? MultiMsgIndex : MsgIndex;
  445.     if(style != old_style){
  446.             state->mangled_header = 1;
  447.             state->mangled_footer = 1;
  448.         old_style = style;
  449.         id.msg_at_top = 0L;
  450.     }
  451.  
  452.         /*------------ Update the title bar -----------*/
  453.     if(state->mangled_header) {
  454.             do_index_border(cntxt, folder, stream, msgmap, style, NULL,
  455.                 INDX_HEADER);
  456.         state->mangled_header = 0;
  457.     } 
  458.     else if(mn_get_total(msgmap) > 0) {
  459.         /*
  460.          * No fetchstructure necessary before mail_elt as the elt's
  461.          * should have been loaded with the correct flags when we did
  462.          * a fetchflags as part of get_lflags the first time we painted
  463.          * the index...
  464.          */
  465.         update_titlebar_message();
  466.             update_titlebar_status();
  467.     }
  468.  
  469.     current_index_state = &id;
  470.  
  471.         /*------------ draw the index body ---------------*/
  472.     cur_row = update_index(state, &id);
  473.     if(F_OFF(F_SHOW_CURSOR, state)){
  474.         cur_row = state->ttyo->screen_rows - FOOTER_ROWS(state);
  475.         cur_col = 0;
  476.     }
  477.     else if(id.status_col >= 0)
  478.       cur_col = min(id.status_col, state->ttyo->screen_cols-1);
  479.  
  480.         ps_global->redrawer = redraw_index_body;
  481.  
  482.         /*------------ draw the footer/key menus ---------------*/
  483.     if(state->mangled_footer) {
  484.             if(!state->painted_footer_on_startup){
  485.         if(km_popped){
  486.             FOOTER_ROWS(state) = 3;
  487.             clearfooter(state);
  488.         }
  489.  
  490.         do_index_border(cntxt, folder, stream, msgmap, style,
  491.                   &which_keys, INDX_FOOTER);
  492.         if(km_popped){
  493.             FOOTER_ROWS(state) = 1;
  494.             mark_keymenu_dirty();
  495.         }
  496.         }
  497.  
  498.         state->mangled_footer = 0;
  499.     }
  500.  
  501.         state->painted_body_on_startup   = 0;
  502.         state->painted_footer_on_startup = 0;
  503.  
  504.     /*-- Display any queued message (eg, new mail, command result --*/
  505.     if(km_popped){
  506.         FOOTER_ROWS(state) = 3;
  507.         mark_status_unknown();
  508.     }
  509.  
  510.         display_message(ch);
  511.     if(km_popped){
  512.         FOOTER_ROWS(state) = 1;
  513.         mark_status_unknown();
  514.     }
  515.  
  516.     if(F_ON(F_SHOW_CURSOR, state) && cur_row < 0){
  517.         q_status_message(SM_ORDER,
  518.         (ch==NO_OP_IDLE || ch==NO_OP_COMMAND) ? 0 : 3, 5,
  519.         "No messages in folder");
  520.         cur_row = state->ttyo->screen_rows - FOOTER_ROWS(state);
  521.         display_message(ch);
  522.     }
  523.  
  524.     MoveCursor(cur_row, cur_col);
  525.  
  526.         /* Let read_command do the fflush(stdout) */
  527.  
  528.         /*---------- Read command and validate it ----------------*/
  529. #ifdef    MOUSE
  530.     if(stream == ps_global->mail_stream){    /* prime the handler */
  531.         mouse_in_content(KEY_MOUSE, -1, -1, 0x5, 0);
  532.         register_mfunc(mouse_in_content, HEADER_ROWS(ps_global), 0,
  533.                state->ttyo->screen_rows-(FOOTER_ROWS(ps_global)+1),
  534.                state->ttyo->screen_cols);
  535.                
  536.     }
  537. #endif
  538. #if defined(DOS) || defined(OS2)
  539.     /*
  540.      * AND pre-build header lines.  This works just fine under
  541.      * DOS since we wait for characters in a loop. Something will
  542.          * will have to change under UNIX if we want to do the same.
  543.      */
  544.     while_waiting = build_header_cache;
  545. #ifdef    _WINDOWS
  546.     while_waiting = NULL;
  547.     mswin_setscrollcallback (index_scroll_callback);
  548. #endif
  549. #endif
  550.     ch = read_command();
  551. #ifdef    MOUSE
  552.     if(stream == ps_global->mail_stream)
  553.       clear_mfunc(mouse_in_content);
  554. #endif
  555. #if defined(DOS) || defined(OS2)
  556.     while_waiting = NULL;
  557. #ifdef    _WINDOWS
  558.     mswin_setscrollcallback(NULL);
  559. #endif
  560. #endif
  561.  
  562.         orig_ch = ch;
  563.  
  564.     if(ch < 0x0100 && isupper((unsigned char)ch))    /* force lower case */
  565.       ch = tolower((unsigned char)ch);
  566.     else if(ch >= PF1 && ch <= PF12 && which_keys > 0 && which_keys < 4)
  567.       ch = (which_keys == 1) ? PF2OPF(ch) :    /* map f-key to menu page */
  568.         (which_keys == 2) ? PF2OOPF(ch) : PF2OOOPF(ch);
  569.  
  570.     ch = validatekeys(ch);
  571.  
  572.     if(km_popped)
  573.       switch(ch){
  574.         case NO_OP_IDLE:
  575.         case NO_OP_COMMAND: 
  576.         case PF2:
  577.         case OPF2:
  578.             case OOPF2:
  579.             case OOOPF2:
  580.         case 'o' :
  581.         case KEY_RESIZE:
  582.         case ctrl('L'):
  583.           km_popped++;
  584.           break;
  585.         
  586.         default:
  587.           clearfooter(state);
  588.           break;
  589.       }
  590.  
  591.     /*----------- Execute the command ------------------*/
  592.     switch(ch) {
  593.  
  594.             /*---------- Roll keymenu ----------*/
  595.           case PF2:
  596.           case OPF2:
  597.           case OOPF2:
  598.           case OOOPF2:
  599.       case 'o':
  600.             if(ps_global->anonymous) {
  601.           if(ch == PF2)
  602.         ch = 'w';
  603.               goto df;
  604.         }
  605.             if(ch == 'o')
  606.           warn_other_cmds();
  607.         what_keymenu = NextTwelve;
  608.         state->mangled_footer = 1;
  609.         break;
  610.  
  611.  
  612.             /*---------- Scroll back up ----------*/
  613.       case PF7:
  614.       case '-' :
  615.           case ctrl('Y'): 
  616.       case KEY_PGUP:
  617.       pageup:
  618.         j = -1L;
  619.         for(k = i = id.msg_at_top; ; i--){
  620.         if(!get_lflag(stream, msgmap, i, MN_HIDE)){
  621.             k = i;
  622.             if(++j >= id.lines_per_page){
  623.             if((id.msg_at_top = i) == 1L)
  624.               q_status_message(SM_ORDER, 0, 1, "First Index page");
  625.  
  626.             break;
  627.             }
  628.            }
  629.  
  630.         if(i <= 1L){
  631.             if(mn_get_cur(msgmap) == 1L)
  632.               q_status_message(SM_ORDER, 0, 1,
  633.               "Already at start of Index");
  634.  
  635.             break;
  636.         }
  637.         }
  638.  
  639.         if(mn_total_cur(msgmap) == 1L)
  640.           mn_set_cur(msgmap, k);
  641.  
  642.         break;
  643.  
  644.  
  645.             /*---------- Scroll forward, next page ----------*/
  646.       case PF8:
  647.       case '+':
  648.           case ctrl('V'): 
  649.       case KEY_PGDN:
  650.       case ' ':
  651.       pagedown:
  652.         j = -1L;
  653.         for(k = i = id.msg_at_top; ; i++){
  654.         if(!get_lflag(stream, msgmap, i, MN_HIDE)){
  655.             k = i;
  656.             if(++j >= id.lines_per_page){
  657.             if(i+id.lines_per_page >= mn_get_total(msgmap))
  658.               q_status_message(SM_ORDER, 0, 1, "Last Index page");
  659.  
  660.             id.msg_at_top = i;
  661.             break;
  662.             }
  663.         }
  664.  
  665.         if(i >= mn_get_total(msgmap)){
  666.             if(mn_get_cur(msgmap) == k)
  667.               q_status_message(SM_ORDER,0,1,"Already at end of Index");
  668.  
  669.             break;
  670.         }
  671.         }
  672.  
  673.         if(mn_total_cur(msgmap) == 1L)
  674.           mn_set_cur(msgmap, k);
  675.  
  676.         break;
  677.  
  678. #ifdef MOUSE        
  679.       case KEY_MOUSE:
  680.         {
  681.           static int lastWind;
  682.           MOUSEPRESS mp;
  683.           void    *text;
  684.           long    len;
  685.           int    format;
  686.           
  687.  
  688.           mouse_get_last (NULL, &mp);
  689.           mp.row -= 2;
  690.           if (mp.doubleclick && mp.button != M_BUTTON_RIGHT) {
  691.           orig_ch = ch = F_ON(F_USE_FK, ps_global) ? PF4 : 'v';
  692.           goto df;
  693.           }
  694.           else{
  695.         for(i = id.msg_at_top;
  696.             mp.row >= 0 && i <= mn_get_total(msgmap);
  697.             i++)
  698.           if(!get_lflag(stream, msgmap, i, MN_HIDE)){
  699.               mp.row--;
  700.               if(mn_total_cur(msgmap) == 1L)
  701.             mn_set_cur(msgmap, i);
  702.           }
  703. #ifdef _WINDOWS             
  704.         if (mp.button == M_BUTTON_RIGHT) {
  705.           char title[GETTEXT_TITLELEN+1];
  706.  
  707.             /* Launch text in alt window. */
  708.           if (mp.flags & M_KEY_CONTROL)
  709.             lastWind = 0;
  710.           if (index_gettext_callback (title, &text, &len, &format)) {
  711.             if (format == GETTEXT_TEXT) 
  712.               lastWind = mswin_displaytext (title, text, (size_t)len, 
  713.                           NULL, lastWind, 0);
  714.             else if (format == GETTEXT_LINES) 
  715.               lastWind = mswin_displaytext (title, NULL, 0, 
  716.                           text, lastWind, 0);
  717.           }
  718.         }
  719. #endif /* _WINDOWS */
  720.           }
  721.         }
  722.  
  723.         break;
  724. #endif    /* MOUSE */
  725.  
  726.             /*---------- Redraw/resize ----------*/
  727.           case KEY_RESIZE:
  728.         clear_index_cache();
  729.       case ctrl('L'):
  730.         mark_status_dirty();
  731.         mark_keymenu_dirty();
  732.         mark_titlebar_dirty();
  733.             state->mangled_screen = 1;        /* force repaint and... */
  734.         if(ch == ctrl('L'))
  735.           force = 1;            /* check for new mail on ^L */
  736.  
  737.             break;
  738.  
  739.  
  740.             /*---------- No op command ----------*/
  741.           case NO_OP_IDLE:
  742.       case NO_OP_COMMAND:
  743.             break;    /* no op check for new mail */
  744.  
  745.  
  746.             /*---------- Default -- all other command ----------*/
  747.           default:
  748.           df:
  749.         if((ch == '?' || ch == ctrl('G') || ch == PF1
  750.             || ch == OPF1 || ch == OOPF1 || ch == OOOPF1)
  751.         && FOOTER_ROWS(state) == 1
  752.         && km_popped == 0){
  753.         km_popped = 2;
  754.         mark_status_unknown();
  755.         mark_keymenu_dirty();
  756.         state->mangled_footer = 1;
  757.         }
  758.         else if(stream == state->mail_stream){
  759.         process_cmd(state, msgmap, ch,
  760.                 (style == MsgIndex || style == MultiMsgIndex)?1:2,
  761.                 orig_ch, &force);
  762.         if(state->next_screen != SCREEN_FUN_NULL){
  763.             ps_global->redrawer = NULL;
  764.             current_index_state = NULL;
  765.             if(id.entry_state)
  766.               fs_give((void **)&(id.entry_state));
  767.  
  768.             return(0);
  769.         }
  770.         else{
  771.             if(stream != state->mail_stream){
  772.             /*
  773.              * Must have had an failed open.  repair our
  774.              * pointers...
  775.              */
  776.             id.stream = stream = state->mail_stream;
  777.             id.msgmap = msgmap = state->msgmap;
  778.             }
  779.  
  780.             current_index_state = &id;
  781.         }
  782.  
  783.         /*
  784.          * Page framing exception handling here.  If we
  785.          * did something that should scroll-by-a-line, frame
  786.          * the page by hand here rather than leave it to the
  787.          * page-by-page framing in update_index()...
  788.          */
  789.         switch(ch){
  790.           case KEY_DOWN:
  791.           case ctrl('N'):
  792.             for(j = 0L, k = i = id.msg_at_top; ; i++){
  793.             if(!get_lflag(stream, msgmap, i, MN_HIDE)){
  794.                 k = i;
  795.                 if(j++ >= id.lines_per_page)
  796.                   break;
  797.             }
  798.  
  799.             if(i >= mn_get_total(msgmap)){
  800.                 k = 0L;        /* don't scroll */
  801.                 break;
  802.             }
  803.             }
  804.  
  805.             if(k && (mn_get_cur(msgmap) + HS_MARGIN(state)) >= k)
  806.               index_scroll_down(1L);
  807.  
  808.             break;
  809.  
  810.           case ctrl('P'):
  811.           case KEY_UP:
  812.             if(mn_get_cur(msgmap) < (id.msg_at_top + HS_MARGIN(state)))
  813.               index_scroll_up(1L);
  814.  
  815.             break;
  816.  
  817.           default:
  818.             break;
  819.         }
  820.         }
  821.         else{            /* special processing */
  822.         switch(ch){
  823.           case '?':        /* help! */
  824.           case PF1:
  825.           case ctrl('G'):
  826.             helper(h_simple_index,
  827.                (!strcmp(folder, INTERRUPTED_MAIL))
  828.                  ? "HELP FOR SELECTING INTERRUPTED MSG"
  829.                  : "HELP FOR SELECTING POSTPONED MSG",
  830.                1);
  831.             state->mangled_screen = 1;
  832.             break;
  833.  
  834.           case 'd':        /* delete */
  835.           case 'D':
  836.           case PF9:
  837.             dprint(3,(debugfile, "Special delete: msg %s\n",
  838.                   long2string(mn_get_cur(msgmap))));
  839.             {
  840.             MESSAGECACHE *mc;
  841.             long          raw;
  842.             int          del = 0;
  843.  
  844.             raw = mn_m2raw(msgmap, mn_get_cur(msgmap));
  845.             if(!mail_elt(stream, raw)->deleted){
  846.                 clear_index_cache_ent(mn_get_cur(msgmap));
  847.                 mail_setflag(stream,long2string(raw),"\\DELETED");
  848.                 update_titlebar_status();
  849.                 del++;
  850.             }
  851.  
  852.             q_status_message2(SM_ORDER, 0, 1,
  853.                       "Message %s %sdeleted",
  854.                       long2string(mn_get_cur(msgmap)),
  855.                       (del) ? "" : "already ");
  856.             }
  857.  
  858.             break;
  859.  
  860.           case 'u':        /* UNdelete */
  861.           case 'U':
  862.           case PF10:
  863.             dprint(3,(debugfile, "Special UNdelete: msg %s\n",
  864.                   long2string(mn_get_cur(msgmap))));
  865.             {
  866.             MESSAGECACHE *mc;
  867.             long          raw;
  868.             int          del = 0;
  869.  
  870.             raw = mn_m2raw(msgmap, mn_get_cur(msgmap));
  871.             if(mail_elt(stream, raw)->deleted){
  872.                 clear_index_cache_ent(mn_get_cur(msgmap));
  873.                 mail_clearflag(stream, long2string(raw),
  874.                        "\\DELETED");
  875.                 update_titlebar_status();
  876.                 del++;
  877.             }
  878.  
  879.             q_status_message2(SM_ORDER, 0, 1,
  880.                       "Message %s %sdeleted",
  881.                       long2string(mn_get_cur(msgmap)),
  882.                       (del) ? "UN" : "NOT ");
  883.             }
  884.  
  885.             break;
  886.  
  887.           case 'e':        /* exit */
  888.           case 'E':
  889.           case PF3:
  890.             ps_global->redrawer = NULL;
  891.             current_index_state = NULL;
  892.             if(id.entry_state)
  893.               fs_give((void **)&(id.entry_state));
  894.  
  895.             return(1);
  896.             break;
  897.  
  898.           case 's':        /* select */
  899.           case ctrl('M'):
  900.           case ctrl('J'):
  901.           case PF4:
  902.             ps_global->redrawer = NULL;
  903.             current_index_state = NULL;
  904.             if(id.entry_state)
  905.               fs_give((void **)&(id.entry_state));
  906.  
  907.             return(0);
  908.  
  909.           case 'p':        /* previous */
  910.           case ctrl('P'):
  911.           case KEY_UP:
  912.           case PF5:
  913.             mn_dec_cur(stream, msgmap);
  914.             break;
  915.  
  916.           case 'n':        /* next */
  917.           case ctrl('N'):
  918.           case KEY_DOWN:
  919.           case PF6:
  920.             mn_inc_cur(stream, msgmap);
  921.             break;
  922.  
  923.           default :
  924.             bogus_command(ch, NULL);
  925.             break;
  926.         }
  927.         }
  928.     }                /* The big switch */
  929.     }                    /* the BIG while loop! */
  930. }
  931.  
  932.  
  933.  
  934. /*----------------------------------------------------------------------
  935.   Manage index body painting
  936.  
  937.   Args: state - pine struct containing selected message data
  938.     index_state - struct describing what's currently displayed
  939.  
  940.   Returns: screen row number of first highlighted message
  941.  
  942.   The idea is pretty simple.  Maintain an array of index line id's that
  943.   are displayed and their hilited state.  Decide what's to be displayed
  944.   and update the screen appropriately.  All index screen painting
  945.   is done here.  Pretty simple, huh?
  946.  ----*/
  947. int
  948. update_index(state, screen)
  949.     struct pine         *state;
  950.     struct index_state  *screen;
  951. {
  952.     int  i, agg, retval = -1;
  953.     long n;
  954.  
  955.     if(!screen)
  956.       return(-1);
  957.  
  958. #ifdef _WINDOWS
  959.     mswin_beginupdate();
  960. #endif
  961.  
  962.     /*---- reset the works if necessary ----*/
  963.     if(state->mangled_body && screen->entry_state){
  964.     fs_give((void **)&(screen->entry_state));
  965.     screen->lines_per_page = 0;
  966.     ClearBody();
  967.     }
  968.  
  969.     state->mangled_body = 0;
  970.  
  971.     /*---- make sure we have a place to write state ----*/
  972.     if(screen->lines_per_page
  973.     != max(0, state->ttyo->screen_rows - FOOTER_ROWS(state)
  974.                        - HEADER_ROWS(state))){
  975.     i = screen->lines_per_page;
  976.     screen->lines_per_page
  977.         = max(0, state->ttyo->screen_rows - FOOTER_ROWS(state)
  978.                           - HEADER_ROWS(state));
  979.     if(!i){
  980.         size_t len = screen->lines_per_page * sizeof(struct entry_state);
  981.         screen->entry_state = (struct entry_state *) fs_get(len);
  982.     }
  983.     else
  984.       fs_resize((void **)&(screen->entry_state),
  985.             (size_t)screen->lines_per_page);
  986.  
  987.     for(; i < screen->lines_per_page; i++)    /* init new entries */
  988.       screen->entry_state[i].id = -1;
  989.     }
  990.  
  991.     /*---- figure out the first message on the display ----*/
  992.     if(screen->msg_at_top < 1L
  993.        || (any_lflagged(screen->msgmap, MN_HIDE) > 0L
  994.        && get_lflag(screen->stream, screen->msgmap,
  995.             screen->msg_at_top, MN_HIDE))){
  996.     screen->msg_at_top = top_ent_calc(screen->stream, screen->msgmap,
  997.                       screen->msg_at_top,
  998.                       screen->lines_per_page);
  999.     }
  1000.     else if(mn_get_cur(screen->msgmap) < screen->msg_at_top){
  1001.     long i, j, k;
  1002.  
  1003.     /* scroll back a page at a time until current is displayed */
  1004.     while(mn_get_cur(screen->msgmap) < screen->msg_at_top){
  1005.         for(i = screen->lines_per_page, j = screen->msg_at_top-1L, k = 0L;
  1006.         i > 0L && j > 0L;
  1007.         j--)
  1008.           if(!get_lflag(screen->stream, screen->msgmap, j, MN_HIDE)){
  1009.           k = j;
  1010.           i--;
  1011.           }
  1012.  
  1013.         if(i == screen->lines_per_page)
  1014.           break;                /* can't scroll back ? */
  1015.         else
  1016.           screen->msg_at_top = k;
  1017.     }
  1018.     }
  1019.     else if(mn_get_cur(screen->msgmap) >= screen->msg_at_top
  1020.                              + screen->lines_per_page){
  1021.     long i, j, k;
  1022.  
  1023.     while(1){
  1024.         for(i = screen->lines_per_page, j = k = screen->msg_at_top;
  1025.         j <= mn_get_total(screen->msgmap) && i > 0L;
  1026.         j++)
  1027.           if(!get_lflag(screen->stream, screen->msgmap, j, MN_HIDE)){
  1028.           k = j;
  1029.           i--;
  1030.           }
  1031.  
  1032.         if(mn_get_cur(screen->msgmap) <= k)
  1033.           break;
  1034.         else{
  1035.         /* set msg_at_top to next displayed message */
  1036.         for(i = k + 1L; i <= mn_get_total(screen->msgmap); i++)
  1037.           if(!get_lflag(screen->stream, screen->msgmap, i, MN_HIDE)){
  1038.               k = i;
  1039.               break;
  1040.           }
  1041.  
  1042.         screen->msg_at_top = k;
  1043.         }
  1044.     }
  1045.     }
  1046.  
  1047. #ifdef    _WINDOWS
  1048.     /* Set scroll range and position.  Note that message numbers start at 1
  1049.      * while scroll position starts at 0. */
  1050.     scroll_setrange(mn_get_total(screen->msgmap) - 1L);
  1051.     scroll_setpos(screen->msg_at_top - 1L);
  1052. #endif
  1053.  
  1054.     /*---- march thru display lines, painting whatever is needed ----*/
  1055.     for(i = 0, n = screen->msg_at_top; i < (int) screen->lines_per_page; i++){
  1056.     if(n < 1 || n > mn_get_total(screen->msgmap)){
  1057.         if(screen->entry_state[i].id){
  1058.         screen->entry_state[i].hilite = 0;
  1059.         screen->entry_state[i].bolded = 0;
  1060.         screen->entry_state[i].id     = 0L;
  1061.         ClearLine(HEADER_ROWS(state) + i);
  1062.         }
  1063.     }
  1064.     else{
  1065.         int      cur = mn_is_cur(screen->msgmap, n),
  1066.              sel = get_lflag(screen->stream,screen->msgmap,n,MN_SLCT),
  1067.              status_col;
  1068.         HLINE_S *h   = build_header_line(state,
  1069.                 screen->stream,screen->msgmap,n);
  1070.  
  1071.         status_col = screen->status_col;
  1072.  
  1073.         if(h->id != screen->entry_state[i].id
  1074.            || (cur != screen->entry_state[i].hilite)
  1075.            || (sel != screen->entry_state[i].bolded)){
  1076.         MoveCursor(HEADER_ROWS(state) + i, 0);
  1077.         if(F_ON(F_FORCE_LOW_SPEED,ps_global) || ps_global->low_speed){
  1078.             MoveCursor(HEADER_ROWS(state) + i, status_col);
  1079.             Writechar((sel) ? 'X' :
  1080.                     (cur && h->line[status_col] == ' ') ?
  1081.                     '-' : h->line[status_col], 0);
  1082.             Writechar((cur) ? '>' : h->line[status_col+1], 0);
  1083.  
  1084.             if(h->id != screen->entry_state[i].id){
  1085.             if(status_col == 0)
  1086.               PutLine0(HEADER_ROWS(state) + i, 2, &h->line[2]);
  1087.             else{ /* this will rarely be set up this way */
  1088.                 char save_char1, save_char2;
  1089.  
  1090.                 save_char1 = h->line[status_col];
  1091.                 save_char2 = h->line[status_col+1];
  1092.                 h->line[status_col] = (sel) ? 'X' :
  1093.                 (cur && save_char1 == ' ') ?
  1094.                  '-' : save_char1;
  1095.                 h->line[status_col+1] = (cur) ? '>' :
  1096.                                  save_char2;
  1097.                 PutLine0(HEADER_ROWS(state) + i, 0, &h->line[0]);
  1098.                 h->line[status_col]   = save_char1;
  1099.                 h->line[status_col+1] = save_char2;
  1100.             }
  1101.             }
  1102.         }
  1103.         else{
  1104.             char *draw = h->line;
  1105.             char  save_char;
  1106.             int   drew_X = 0;
  1107.  
  1108.             if(cur)
  1109.               StartInverse();
  1110.             
  1111.             save_char = draw[status_col];
  1112.  
  1113.             if(sel && (F_OFF(F_SELECTED_SHOWN_BOLD, state)
  1114.                    || !StartBold())){
  1115.             draw[status_col] = 'X';
  1116.             drew_X++;
  1117.             }
  1118.  
  1119.             Write_to_screen(draw);
  1120.             if(drew_X)
  1121.               draw[status_col] = save_char;
  1122.  
  1123.             if(sel && !drew_X)
  1124.               EndBold();
  1125.  
  1126.             if(cur)
  1127.               EndInverse();
  1128.         }
  1129.         }
  1130.  
  1131.         screen->entry_state[i].hilite = cur;
  1132.         screen->entry_state[i].bolded = sel;
  1133.         screen->entry_state[i].id     = h->id;
  1134.  
  1135.         if(cur && retval < 0L)
  1136.           retval = i + HEADER_ROWS(state);
  1137.     }
  1138.  
  1139.     /*--- increment n ---*/
  1140.     while(++n <= mn_get_total(screen->msgmap)
  1141.           && get_lflag(screen->stream, screen->msgmap, n, MN_HIDE))
  1142.       ;
  1143.  
  1144.     }
  1145.  
  1146. #ifdef _WINDOWS
  1147.     mswin_endupdate();
  1148. #endif
  1149.     fflush(stdout);
  1150.     return(retval);
  1151. }
  1152.  
  1153.  
  1154.  
  1155. /*----------------------------------------------------------------------
  1156.      Scroll to specified postion.
  1157.  
  1158.  
  1159.   Args: paint - TRUE when this function shoult repaint the screen.
  1160.     pos - position to scroll to.
  1161.  
  1162.   Returns: TRUE - did the scroll operation.
  1163.        FALSE - was not able to do the scroll operation.
  1164.  ----*/
  1165. int
  1166. index_scroll_to_pos (pos)
  1167. long    pos;
  1168. {
  1169.     static short bad_timing = 0;
  1170.     long    i, j, k;
  1171.     
  1172.     if(bad_timing)
  1173.       return (FALSE);
  1174.  
  1175.     /*
  1176.      * Put the requested line at the top of the screen...
  1177.      */
  1178. #if 1
  1179.     /*
  1180.      * Starting at msg 'pos' find next visable message.
  1181.      */
  1182.     for(i=pos; i <= mn_get_total(current_index_state->msgmap); i++) {
  1183.       if(!get_lflag(current_index_state->stream, 
  1184.                 current_index_state->msgmap, i, MN_HIDE)){
  1185.       current_index_state->msg_at_top = i;
  1186.       break;
  1187.       }
  1188.     }
  1189. #else
  1190.     for(i=1L, j=pos; i <= mn_get_total(current_index_state->msgmap); i++) {
  1191.       if(!get_lflag(current_index_state->stream, 
  1192.                 current_index_state->msgmap, i, MN_HIDE)){
  1193.       if((current_index_state->msg_at_top = i) > 
  1194.           mn_get_cur(current_index_state->msgmap))
  1195.         mn_set_cur(current_index_state->msgmap, i);
  1196.  
  1197.       if(--j <= 0L)
  1198.         break;
  1199.       }
  1200.     }
  1201. #endif
  1202.  
  1203.     /*
  1204.      * If single selection, move selected message to be on the sceen.
  1205.      */
  1206.     if (mn_total_cur(current_index_state->msgmap) == 1L) {
  1207.       if (current_index_state->msg_at_top > 
  1208.                   mn_get_cur (current_index_state->msgmap)) {
  1209.     /* Selection was above screen, move to top of screen. */
  1210.     mn_set_cur (current_index_state->msgmap, 
  1211.                     current_index_state->msg_at_top);
  1212.       }
  1213.       else {
  1214.     /* Scan through the screen.  If selection found, leave where is.
  1215.      * Otherwise, move to end of screen */
  1216.         for(  i = current_index_state->msg_at_top, 
  1217.             j = current_index_state->lines_per_page;
  1218.           i != mn_get_cur(current_index_state->msgmap) && 
  1219.         i <= mn_get_total(current_index_state->msgmap) && 
  1220.         j > 0L;
  1221.           i++) {
  1222.         if(!get_lflag(current_index_state->stream, 
  1223.                 current_index_state->msgmap, i, MN_HIDE)){
  1224.             j--;
  1225.             k = i;
  1226.             }
  1227.         }
  1228.     if(j <= 0L)
  1229.         /* Move to end of screen. */
  1230.         mn_set_cur(current_index_state->msgmap, k);
  1231.       }
  1232.     }
  1233.  
  1234.     bad_timing = 0;
  1235.     return (TRUE);
  1236. }
  1237.  
  1238.  
  1239.  
  1240. /*----------------------------------------------------------------------
  1241.      Adjust the index display state down a line, and repaint.
  1242.  
  1243.  
  1244.   Returns: TRUE - did the scroll operation.
  1245.        FALSE - was not able to do the scroll operation.
  1246.  ----*/
  1247. int
  1248. index_scroll_down(scroll_count)
  1249.     long scroll_count;
  1250. {
  1251.     static short bad_timing = 0;
  1252.     long i, j, k;
  1253.     long cur, total;
  1254.  
  1255.     if(bad_timing)
  1256.       return (FALSE);
  1257.  
  1258.     bad_timing = 1;
  1259.     
  1260.     
  1261.     j = -1L;
  1262.     total = mn_get_total (current_index_state->msgmap);
  1263.     for(k = i = current_index_state->msg_at_top; ; i++){
  1264.  
  1265.     /* Only examine non-hidden messages. */
  1266.         if(!get_lflag(current_index_state->stream, 
  1267.               current_index_state->msgmap, i, MN_HIDE)){
  1268.         /* Remember this message */
  1269.         k = i;
  1270.         /* Increment count of lines.  */
  1271.         if (++j >= scroll_count) {
  1272.         /* Counted enough lines, stop. */
  1273.         current_index_state->msg_at_top = k;
  1274.         break;
  1275.         }
  1276.     }
  1277.         
  1278.     /* If at last message, stop. */
  1279.     if (i >= total){
  1280.         current_index_state->msg_at_top = k;
  1281.         break;
  1282.     }
  1283.     }
  1284.  
  1285.     /*
  1286.      * If not multiple selection, see if selected message visable.  if not
  1287.      * set it to last visable message. 
  1288.      */
  1289.     if(mn_total_cur(current_index_state->msgmap) == 1L) {
  1290.     j = 0L;
  1291.     cur = mn_get_cur (current_index_state->msgmap);
  1292.     for (i = current_index_state->msg_at_top; i <= total; ++i) {
  1293.         if(!get_lflag(current_index_state->stream, 
  1294.                   current_index_state->msgmap, i, MN_HIDE)) {
  1295.             if (++j >= current_index_state->lines_per_page) {
  1296.             break;
  1297.             }
  1298.         if (i == cur) 
  1299.             break;
  1300.         }
  1301.         }
  1302.     if (i != cur) 
  1303.         mn_set_cur(current_index_state->msgmap, 
  1304.                         current_index_state->msg_at_top);
  1305.     }
  1306.  
  1307.     bad_timing = 0;
  1308.     return (TRUE);
  1309. }
  1310.  
  1311.  
  1312.  
  1313. /*----------------------------------------------------------------------
  1314.      Adjust the index display state up a line
  1315.  
  1316.   Args: paint - TRUE when this function shoult repaint the screen.
  1317.  
  1318.   Returns: TRUE - did the scroll operation.
  1319.        FALSE - was not able to do the scroll operation.
  1320.  
  1321.  ----*/
  1322. int
  1323. index_scroll_up(scroll_count)
  1324.     long scroll_count;
  1325. {
  1326.     static short bad_timing = 0;
  1327.     long i, j, k;
  1328.     long cur;
  1329.  
  1330.     if(bad_timing)
  1331.       return(FALSE);
  1332.  
  1333.     bad_timing = 1;
  1334.     
  1335.     j = -1L;
  1336.     for(k = i = current_index_state->msg_at_top; ; i--){
  1337.  
  1338.     /* Only examine non-hidden messages. */
  1339.         if(!get_lflag(current_index_state->stream, 
  1340.               current_index_state->msgmap, i, MN_HIDE)){
  1341.         /* Remember this message */
  1342.         k = i;
  1343.         /* Increment count of lines.  */
  1344.         if (++j >= scroll_count) {
  1345.         /* Counted enough lines, stop. */
  1346.         current_index_state->msg_at_top = k;
  1347.         break;
  1348.         }
  1349.     }
  1350.         
  1351.     /* If at first message, stop */
  1352.     if (i <= 1L){
  1353.         current_index_state->msg_at_top = k;
  1354.         break;
  1355.     }
  1356.     }
  1357.  
  1358.     
  1359.     /*
  1360.      * If not multiple selection, see if selected message visable.  if not
  1361.      * set it to last visable message. 
  1362.      */
  1363.     if(mn_total_cur(current_index_state->msgmap) == 1L) {
  1364.     j = 0L;
  1365.     cur = mn_get_cur (current_index_state->msgmap);
  1366.     for (    i = current_index_state->msg_at_top; 
  1367.         i <= mn_get_total(current_index_state->msgmap);
  1368.         ++i) {
  1369.         if(!get_lflag(current_index_state->stream, 
  1370.                   current_index_state->msgmap, i, MN_HIDE)) {
  1371.             if (++j >= current_index_state->lines_per_page) {
  1372.             k = i;
  1373.             break;
  1374.             }
  1375.         if (i == cur) 
  1376.             break;
  1377.         }
  1378.         }
  1379.     if (i != cur) 
  1380.         mn_set_cur(current_index_state->msgmap, k);
  1381.     }
  1382.  
  1383.  
  1384.     bad_timing = 0;
  1385.     return (TRUE);
  1386. }
  1387.  
  1388.  
  1389.  
  1390. /*----------------------------------------------------------------------
  1391.      Calculate the message number that should be at the top of the display
  1392.  
  1393.   Args: current - the current message number
  1394.         lines_per_page - the number of lines for the body of the index only
  1395.  
  1396.   Returns: -1 if the current message is -1 
  1397.            the message entry for the first message at the top of the screen.
  1398.  
  1399. When paging in the index it is always on even page boundies, and the
  1400. current message is always on the page thus the top of the page is
  1401. completely determined by the current message and the number of lines
  1402. on the page. 
  1403.  ----*/
  1404. long
  1405. top_ent_calc(stream, msgs, at_top, lines_per_page)
  1406.      MAILSTREAM *stream;
  1407.      MSGNO_S *msgs;
  1408.      long     at_top, lines_per_page;
  1409. {
  1410.     long current;
  1411.  
  1412.     current = (mn_total_cur(msgs) <= 1L) ? mn_get_cur(msgs) : at_top;
  1413.  
  1414.     if(current < 0L)
  1415.       return(-1);
  1416.  
  1417.     if(lines_per_page == 0L)
  1418.       return(current);
  1419.  
  1420.     if(any_lflagged(msgs, (MN_HIDE|MN_EXLD))){
  1421.     long n, m = 0L, t = 1L;
  1422.  
  1423.     for(n = 1L; n <= mn_get_total(msgs); n++)
  1424.       if(!get_lflag(stream, msgs, n, MN_HIDE)
  1425.          && (++m % lines_per_page) == 1L){
  1426.           if(n > current)
  1427.         break;
  1428.  
  1429.           t = n;
  1430.       }
  1431.  
  1432.     return(t);
  1433.     }
  1434.     else
  1435.       return(lines_per_page * ((current - 1L)/ lines_per_page) + 1L);
  1436. }
  1437.  
  1438.  
  1439. /*----------------------------------------------------------------------
  1440.       Initialize the index_disp_format array in ps_global from this
  1441.       format string.
  1442.  
  1443.    Args: format -- the string containing the format tokens
  1444.      answer -- put the answer here, free first if there was a previous
  1445.             value here
  1446.  ----*/
  1447. void
  1448. init_index_format(format, answer)
  1449. char         *format;
  1450. INDEX_COL_S **answer;
  1451. {
  1452.     int column = 0;
  1453.  
  1454.     set_need_format_setup();
  1455.     /* if custom format is specified, try it, else go with default */
  1456.     if(!(format && *format && parse_index_format(format, answer))){
  1457.     if(*answer)
  1458.       fs_give((void **)answer);
  1459.  
  1460.     *answer = (INDEX_COL_S *)fs_get(7*sizeof(INDEX_COL_S));
  1461.     memset((void *)(*answer), 0, 7*sizeof(INDEX_COL_S));
  1462.  
  1463.     (*answer)[column].ctype        = iStatus;
  1464.     (*answer)[column].wtype        = Fixed;
  1465.     (*answer)[column++].req_width    = 3;
  1466.  
  1467.     /*
  1468.      * WeCalculate with a non-zero req_width means that this space
  1469.      * will be reserved before calculating the percentages.  It may
  1470.      * get increased or decreased later when we see what we actually need.
  1471.      */
  1472.     (*answer)[column].ctype        = iMessNo;
  1473.     (*answer)[column++].wtype    = WeCalculate;
  1474.  
  1475.     (*answer)[column].ctype        = iDate;
  1476.     (*answer)[column].wtype        = Fixed;
  1477.     (*answer)[column++].req_width    = 6;
  1478.  
  1479.     (*answer)[column].ctype        = iFromTo;
  1480.     (*answer)[column].wtype        = Percent;
  1481.     (*answer)[column++].req_width    = 33; /* percent of rest */
  1482.  
  1483.     (*answer)[column].ctype        = iSize;
  1484.     (*answer)[column++].wtype    = WeCalculate;
  1485.  
  1486.     (*answer)[column].ctype        = iSubject;
  1487.     (*answer)[column].wtype        = Percent;
  1488.     (*answer)[column++].req_width    = 67;
  1489.  
  1490.     (*answer)[column].ctype        = iNothing;
  1491.     }
  1492.  
  1493.     /*
  1494.      * Fill in req_width's for WeCalculate items.
  1495.      */
  1496.     for(column = 0; (*answer)[column].ctype != iNothing; column++){
  1497.     if((*answer)[column].wtype == WeCalculate){
  1498.         switch((*answer)[column].ctype){
  1499.           case iStatus:
  1500.         (*answer)[column].req_width = 3;
  1501.         break;
  1502.           case iFStatus:
  1503.         (*answer)[column].req_width = 6;
  1504.         break;
  1505.           case iMessNo:
  1506.         (*answer)[column].req_width = 3;
  1507.         break;
  1508.           case iDate:
  1509.         (*answer)[column].req_width = 6;
  1510.         break;
  1511.           case iSize:
  1512.         (*answer)[column].req_width = 8;
  1513.         break;
  1514.           case iDescripSize:
  1515.         (*answer)[column].req_width = 9;
  1516.         break;
  1517.         }
  1518.     }
  1519.     }
  1520. }
  1521.  
  1522.  
  1523. struct index_parse_tokens {
  1524.     char        *name;
  1525.     IndexColType ctype;
  1526. };
  1527.  
  1528. struct index_parse_tokens itokens[] = {
  1529.     {"STATUS",      iStatus},
  1530.     {"FULLSTATUS",  iFStatus},
  1531.     {"MSGNO",       iMessNo},
  1532.     {"DATE",        iDate},
  1533.     {"FROMORTO",    iFromTo},
  1534.     {"FROM",        iFrom},
  1535.     {"TO",          iTo},
  1536.     {"SENDER",      iSender},
  1537.     {"SIZE",        iSize},
  1538.     {"DESCRIPSIZE", iDescripSize},
  1539.     {"SUBJECT",     iSubject},
  1540.     {NULL,          iNothing}
  1541. };
  1542.  
  1543. int
  1544. parse_index_format(format_str, answer)
  1545. char         *format_str;
  1546. INDEX_COL_S **answer;
  1547. {
  1548.     int i, column = 0;
  1549.     char *p, *q;
  1550.     struct index_parse_tokens *pt;
  1551.     INDEX_COL_S cdesc[MAXIFLDS]; /* temp storage for answer */
  1552.  
  1553.     get_body_for_index = 0;
  1554.     memset((void *)cdesc, 0, sizeof(cdesc));
  1555.  
  1556.     p = format_str;
  1557.     while(p && *p && column < MAXIFLDS-1){
  1558.     /* skip leading white space for next word */
  1559.     while(p && *p && isspace((unsigned char)*p))
  1560.       p++;
  1561.     
  1562.     /* look for the token this word matches */
  1563.     for(pt = itokens; pt->name; pt++)
  1564.         if(!struncmp(pt->name, p, strlen(pt->name)))
  1565.           break;
  1566.     
  1567.     /* ignore unrecognized word */
  1568.     if(!pt->name){
  1569.         for(q = p; *p && !isspace((unsigned char)*p); p++)
  1570.           ;
  1571.  
  1572.         if(*p)
  1573.           *p++ = '\0';
  1574.  
  1575.         dprint(1, (debugfile,
  1576.                "parse_index_format: unrecognized token: %s\n", q));
  1577.         q_status_message1(SM_ORDER | SM_DING, 0, 3,
  1578.                   "Unrecognized string in index-format: %s", q);
  1579.         continue;
  1580.     }
  1581.  
  1582.     cdesc[column].ctype = pt->ctype;
  1583.     if(pt->ctype == iDescripSize)
  1584.       get_body_for_index = 1;
  1585.  
  1586.     /* skip over name and look for parens */
  1587.     p += strlen(pt->name);
  1588.     if(*p == '('){
  1589.         p++;
  1590.         q = p;
  1591.         while(p && *p && isdigit((unsigned char)*p))
  1592.           p++;
  1593.         
  1594.         if(p && *p && *p == ')' && p > q){
  1595.         cdesc[column].wtype = Fixed;
  1596.         cdesc[column].req_width = atoi(q);
  1597.         }
  1598.         else if(p && *p && *p == '%' && p > q){
  1599.         cdesc[column].wtype = Percent;
  1600.         cdesc[column].req_width = atoi(q);
  1601.         }
  1602.         else{
  1603.         cdesc[column].wtype = WeCalculate;
  1604.         cdesc[column].req_width = 0;
  1605.         }
  1606.     }
  1607.     else{
  1608.         cdesc[column].wtype     = WeCalculate;
  1609.         cdesc[column].req_width = 0;
  1610.     }
  1611.  
  1612.     column++;
  1613.     /* skip text at end of word */
  1614.     while(p && *p && !isspace((unsigned char)*p))
  1615.       p++;
  1616.     }
  1617.  
  1618.     /* if, after all that, we didn't find anything recoznizable, bitch */
  1619.     if(!column){
  1620.     dprint(1, (debugfile, "Completely unrecognizable index-format\n"));
  1621.     q_status_message(SM_ORDER | SM_DING, 0, 3,
  1622.          "Configured \"index-format\" unrecognizable. Using default.");
  1623.     return(0);
  1624.     }
  1625.  
  1626.     /* Finish with Nothing column */
  1627.     cdesc[column].ctype = iNothing;
  1628.  
  1629.     /* free up old answer */
  1630.     if(*answer)
  1631.       fs_give((void **)answer);
  1632.  
  1633.     /* allocate space for new answer */
  1634.     *answer = (INDEX_COL_S *)fs_get((column+1)*sizeof(INDEX_COL_S));
  1635.     memset((void *)(*answer), 0, (column+1)*sizeof(INDEX_COL_S));
  1636.     /* copy answer to real place */
  1637.     for(i = 0; i <= column; i++)
  1638.       (*answer)[i] = cdesc[i];
  1639.  
  1640.     return(1);
  1641. }
  1642.  
  1643.  
  1644. /*----------------------------------------------------------------------
  1645.     This redraws the body of the index screen, taking into
  1646. account any change in the size of the screen. All the state needed to
  1647. repaint is in the static variables so this can be called from
  1648. anywhere.
  1649.  ----*/
  1650. void
  1651. redraw_index_body()
  1652. {
  1653.     int agg;
  1654.  
  1655.     if(agg = (mn_total_cur(current_index_state->msgmap) > 1L))
  1656.       restore_selected(current_index_state->msgmap);
  1657.  
  1658.     ps_global->mangled_body = 1;
  1659.  
  1660.     (void) update_index(ps_global, current_index_state);
  1661.     if(agg)
  1662.       pseudo_selected(current_index_state->msgmap);
  1663. }
  1664.  
  1665.  
  1666.  
  1667. /*----------------------------------------------------------------------
  1668.       Setup the widths of the various columns in the index display
  1669.  
  1670.    Args: news      -- mail stream is news
  1671.      max_msgno -- max message number in mail stream
  1672.  ----*/
  1673. void
  1674. setup_header_widths(cdesc, news, max_msgno)
  1675.     INDEX_COL_S *cdesc;
  1676.     int          news;
  1677.     long         max_msgno;
  1678. {
  1679.     int       i, j, columns, some_to_calculate;
  1680.     int          space_left, screen_width, width, fix, col, scol, altcol;
  1681.     int          keep_going, tot_pct, was_sl;
  1682.     WidthType wtype;
  1683.  
  1684.  
  1685.     dprint(8, (debugfile, "=== setup_header_widths(%d,%ld) ===\n",
  1686.     news, max_msgno));
  1687.  
  1688.     clear_need_format_setup();
  1689.     screen_width = ps_global->ttyo->screen_cols;
  1690.     columns = 0;
  1691.     some_to_calculate = 0;
  1692.     space_left = screen_width;
  1693.  
  1694.     /*
  1695.      * Calculate how many fields there are so we know how many spaces
  1696.      * between columns to reserve.  Fill in Fixed widths now.  Reserve
  1697.      * special case WeCalculate with non-zero req_widths before doing
  1698.      * Percent cases below.
  1699.      */
  1700.     for(i = 0; cdesc[i].ctype != iNothing; i++){
  1701.  
  1702.     /* These aren't included in nr mode */
  1703.     if(ps_global->nr_mode && (cdesc[i].ctype == iFromTo ||
  1704.                   cdesc[i].ctype == iFrom ||
  1705.                   cdesc[i].ctype == iSender ||
  1706.                   cdesc[i].ctype == iTo)){
  1707.         cdesc[i].req_width = 0;
  1708.         cdesc[i].width = 0;
  1709.         cdesc[i].wtype = Fixed;
  1710.     }
  1711.     else if(cdesc[i].wtype == Fixed){
  1712.       cdesc[i].width = cdesc[i].req_width;
  1713.       if(cdesc[i].width > 0)
  1714.         columns++;
  1715.     }
  1716.     else if(cdesc[i].wtype == Percent){
  1717.         cdesc[i].width = 0; /* calculated later */
  1718.         columns++;
  1719.     }
  1720.     else{ /* WeCalculate */
  1721.         cdesc[i].width = cdesc[i].req_width; /* reserve this for now */
  1722.         some_to_calculate++;
  1723.         columns++;
  1724.     }
  1725.  
  1726.     space_left -= cdesc[i].width;
  1727.     }
  1728.  
  1729.     space_left -= (columns - 1); /* space between columns */
  1730.  
  1731.     for(i = 0; cdesc[i].ctype != iNothing; i++){
  1732.     wtype = cdesc[i].wtype;
  1733.     if(wtype != WeCalculate && wtype != Percent && cdesc[i].width == 0)
  1734.       continue;
  1735.  
  1736.     switch(cdesc[i].ctype){
  1737.       case iStatus:
  1738.         cdesc[i].actual_length = 3;
  1739.         cdesc[i].adjustment = Left;
  1740.         break;
  1741.  
  1742.       case iFStatus:
  1743.         cdesc[i].actual_length = 6;
  1744.         cdesc[i].adjustment = Left;
  1745.         break;
  1746.  
  1747.       case iDate:
  1748.         cdesc[i].actual_length = 6;
  1749.         cdesc[i].adjustment = Left;
  1750.         break;
  1751.  
  1752.       case iFromTo:
  1753.       case iFrom:
  1754.       case iSender:
  1755.       case iTo:
  1756.       case iSubject:
  1757.         cdesc[i].adjustment = Left;
  1758.         break;
  1759.  
  1760.       case iMessNo:
  1761.         if(max_msgno < 1000)
  1762.           cdesc[i].actual_length = 3;
  1763.         else if(max_msgno < 10000)
  1764.           cdesc[i].actual_length = 4;
  1765.         else if(max_msgno < 100000)
  1766.           cdesc[i].actual_length = 5;
  1767.         else
  1768.           cdesc[i].actual_length = 6;
  1769.  
  1770.         cdesc[i].adjustment = Right;
  1771.         break;
  1772.  
  1773.       case iSize:
  1774.         if(news)
  1775.           cdesc[i].actual_length = 0;
  1776.         else
  1777.           cdesc[i].actual_length = 8;
  1778.  
  1779.         cdesc[i].adjustment = Right;
  1780.         break;
  1781.  
  1782.       case iDescripSize:
  1783.         if(news)
  1784.           cdesc[i].actual_length = 0;
  1785.         else
  1786.           cdesc[i].actual_length = 9;
  1787.  
  1788.         cdesc[i].adjustment = Right;
  1789.         break;
  1790.     }
  1791.     }
  1792.  
  1793.     /* if have reserved unneeded space for size, give it back */
  1794.     for(i = 0; cdesc[i].ctype != iNothing; i++){
  1795.       if(cdesc[i].ctype == iSize || cdesc[i].ctype == iDescripSize){
  1796.     if(cdesc[i].actual_length == 0){
  1797.       if((fix=cdesc[i].width) > 0){ /* had this reserved */
  1798.         cdesc[i].width = 0;
  1799.         space_left += fix;
  1800.       }
  1801.  
  1802.       space_left++;  /* +1 for space between columns */
  1803.     }
  1804.       }
  1805.     }
  1806.  
  1807.     /*
  1808.      * Calculate the field widths that are basically fixed in width.
  1809.      * Do them in this order in case we don't have enough space to go around.
  1810.      */
  1811.     for(j = 0; j < 6 && space_left > 0 && some_to_calculate; j++){
  1812.       IndexColType targetctype;
  1813.  
  1814.       switch(j){
  1815.     case 0:
  1816.       targetctype = iMessNo;
  1817.       break;
  1818.  
  1819.     case 1:
  1820.       targetctype = iStatus;
  1821.       break;
  1822.  
  1823.     case 2:
  1824.       targetctype = iFStatus;
  1825.       break;
  1826.  
  1827.     case 3:
  1828.       targetctype = iDate;
  1829.       break;
  1830.  
  1831.     case 4:
  1832.       targetctype = iSize;
  1833.       break;
  1834.  
  1835.     case 5:
  1836.       targetctype = iDescripSize;
  1837.       break;
  1838.       }
  1839.  
  1840.       for(i = 0;
  1841.       cdesc[i].ctype != iNothing && space_left >0 && some_to_calculate;
  1842.       i++){
  1843.     if(cdesc[i].ctype == targetctype && cdesc[i].wtype == WeCalculate){
  1844.       some_to_calculate--;
  1845.       fix = min(cdesc[i].actual_length - cdesc[i].width, space_left);
  1846.       cdesc[i].width += fix;
  1847.       space_left -= fix;
  1848.     }
  1849.       }
  1850.     }
  1851.  
  1852.     /*
  1853.      * Fill in widths for Percent cases.  If there are no more to calculate,
  1854.      * use the percentages as relative numbers and use the rest of the space,
  1855.      * else treat them as absolute percentages of the original avail screen.
  1856.      */
  1857.     if(space_left > 0){
  1858.       if(some_to_calculate){
  1859.         for(i = 0; cdesc[i].ctype != iNothing && space_left > 0; i++){
  1860.         if(cdesc[i].wtype == Percent){
  1861.         /* The 2, 200, and +100 are because we're rounding */
  1862.         fix = ((2*cdesc[i].req_width *
  1863.             (screen_width-(columns-1)))+100) / 200;
  1864.             fix = min(fix, space_left);
  1865.             cdesc[i].width += fix;
  1866.             space_left -= fix;
  1867.         }
  1868.     }
  1869.       }
  1870.       else{
  1871.     tot_pct = 0;
  1872.     was_sl = space_left;
  1873.     /* add up total percentages requested */
  1874.         for(i = 0; cdesc[i].ctype != iNothing; i++)
  1875.         if(cdesc[i].wtype == Percent)
  1876.           tot_pct += cdesc[i].req_width;
  1877.  
  1878.     /* give relative weight to requests */
  1879.         for(i = 0;
  1880.         cdesc[i].ctype != iNothing && space_left > 0 && tot_pct > 0;
  1881.         i++){
  1882.         if(cdesc[i].wtype == Percent){
  1883.         fix = ((2*cdesc[i].req_width*was_sl)+tot_pct) / (2*tot_pct);
  1884.             fix = min(fix, space_left);
  1885.             cdesc[i].width += fix;
  1886.             space_left -= fix;
  1887.         }
  1888.     }
  1889.       }
  1890.     }
  1891.  
  1892.     /* split up rest, give twice as much to Subject */
  1893.     keep_going = 1;
  1894.     while(space_left > 0 && keep_going){
  1895.       keep_going = 0;
  1896.       for(i = 0; cdesc[i].ctype != iNothing && space_left > 0; i++){
  1897.     if(cdesc[i].wtype == WeCalculate &&
  1898.       (cdesc[i].ctype == iFromTo ||
  1899.        cdesc[i].ctype == iFrom ||
  1900.        cdesc[i].ctype == iSender ||
  1901.        cdesc[i].ctype == iTo ||
  1902.        cdesc[i].ctype == iSubject)){
  1903.       keep_going++;
  1904.       cdesc[i].width++;
  1905.       space_left--;
  1906.       if(space_left > 0 && cdesc[i].ctype == iSubject){
  1907.           cdesc[i].width++;
  1908.           space_left--;
  1909.       }
  1910.     }
  1911.       }
  1912.     }
  1913.  
  1914.     /* if still more, pad out percent's */
  1915.     keep_going = 1;
  1916.     while(space_left > 0 && keep_going){
  1917.       keep_going = 0;
  1918.       for(i = 0; cdesc[i].ctype != iNothing && space_left > 0; i++){
  1919.     if(cdesc[i].wtype == Percent &&
  1920.       (cdesc[i].ctype == iFromTo ||
  1921.        cdesc[i].ctype == iFrom ||
  1922.        cdesc[i].ctype == iSender ||
  1923.        cdesc[i].ctype == iTo ||
  1924.        cdesc[i].ctype == iSubject)){
  1925.       keep_going++;
  1926.       cdesc[i].width++;
  1927.       space_left--;
  1928.     }
  1929.       }
  1930.     }
  1931.  
  1932.     col = 0;
  1933.     scol = -1;
  1934.     altcol = -1;
  1935.     /* figure out what column is start of status field */
  1936.     for(i = 0; cdesc[i].ctype != iNothing; i++){
  1937.     width = cdesc[i].width;
  1938.     if(width == 0)
  1939.       continue;
  1940.  
  1941.     /* space between columns */
  1942.     if(col > 0)
  1943.       col++;
  1944.  
  1945.     if(cdesc[i].ctype == iStatus){
  1946.         scol = col;
  1947.         break;
  1948.     }
  1949.  
  1950.     if(cdesc[i].ctype == iFStatus){
  1951.         scol = col;
  1952.         break;
  1953.     }
  1954.  
  1955.     if(cdesc[i].ctype == iMessNo)
  1956.       altcol = col;
  1957.  
  1958.     col += width;
  1959.     }
  1960.  
  1961.     if(scol == -1){
  1962.     if(altcol == -1)
  1963.       scol = 0;
  1964.     else
  1965.       scol = altcol;
  1966.     }
  1967.  
  1968.     if(current_index_state)
  1969.       current_index_state->status_col = scol;
  1970. }
  1971.  
  1972.  
  1973.  
  1974. /*----------------------------------------------------------------------
  1975.       Create a string summarizing the message header for index on screen
  1976.  
  1977.    Args: stream -- mail stream to fetch envelope info from
  1978.      msgmap -- message number to pine sort mapping
  1979.      msgno  -- Message number to create line for
  1980.  
  1981.   Result: returns a malloced string
  1982.           saves string in a cache for next call for same header
  1983.  ----*/
  1984. HLINE_S *
  1985. build_header_line(state, stream, msgmap, msgno)
  1986.     struct pine *state;
  1987.     MAILSTREAM  *stream;
  1988.     MSGNO_S     *msgmap;
  1989.     long         msgno;
  1990. {
  1991.     ENVELOPE     *envelope;
  1992.     MESSAGECACHE *cache;
  1993.     ADDRESS      *addr;
  1994.     char          str_buf[MAXIFLDS][MAX_SCREEN_COLS+1], to_us, *field;
  1995.     char         *s_tmp, *buffer, *p, *str;
  1996.     HLINE_S      *hline;
  1997.     struct date   d;
  1998.     INDEX_COL_S  *cdesc;
  1999.     int           i, width, which_array, no_data = 0;
  2000.     BODY *body = NULL, **bodyp;
  2001.  
  2002.  
  2003.     dprint(8, (debugfile, "=== build_header_line(%ld) ===\n", msgno));
  2004.  
  2005.     if(check_need_format_setup())
  2006.       setup_header_widths(state->index_disp_format, IS_NEWS(stream),
  2007.               mn_get_total(msgmap));
  2008.  
  2009.     /* cache hit */
  2010.     if(*(buffer = (hline = get_index_cache(msgno))->line) != '\0') {
  2011.         dprint(9, (debugfile, "Hit: Returning %p -> <%s (%d), %ld>\n",
  2012.            hline, buffer, strlen(buffer), hline->id));
  2013.     return(hline);
  2014.     }
  2015.  
  2016.     bodyp = (get_body_for_index && !IS_NEWS(stream)) ? &body : NULL;
  2017.  
  2018.     envelope = mail_fetchstructure(stream, mn_m2raw(msgmap, msgno), bodyp);
  2019.     cache    = mail_elt(stream, mn_m2raw(msgmap, msgno));
  2020.  
  2021.     if(!envelope || !cache)
  2022.       no_data = 2;
  2023.     /*
  2024.      * Check that the envelope returned has something to display.
  2025.      * If empty, indicate that no message info found.
  2026.      */
  2027.     else if(!envelope->remail && !envelope->return_path && !envelope->date &&
  2028.        !envelope->from && !envelope->sender && !envelope->reply_to &&
  2029.        !envelope->subject && !envelope->to && !envelope->cc &&
  2030.        !envelope->bcc && !envelope->in_reply_to && !envelope->message_id &&
  2031.        !envelope->newsgroups)
  2032.       no_data = 1;
  2033.  
  2034.     cdesc = state->index_disp_format;
  2035.     which_array = 0;
  2036.  
  2037.     /* calculate contents of the required fields */
  2038.     for(i = 0; cdesc[i].ctype != iNothing; i++){
  2039.     width  = cdesc[i].width;
  2040.     if(width == 0)
  2041.       continue;
  2042.  
  2043.     str             = str_buf[which_array++];
  2044.     str[0]          = '\0';
  2045.     cdesc[i].string = str;
  2046.     if(no_data){
  2047.         if(cdesc[i].ctype == iMessNo)
  2048.           sprintf(str, "%ld", msgno);
  2049.         else if(no_data < 2 && cdesc[i].ctype == iSubject)
  2050.           sprintf(str, "%-*.*s", width, width,
  2051.               "[ No Message Text Available ]");
  2052.     }
  2053.     else{
  2054.  
  2055.         switch(cdesc[i].ctype){
  2056.           case iStatus:
  2057.         to_us = (cache->flagged) ? '*' : ' ';
  2058.         for(addr = envelope->to; addr && to_us == ' '; addr=addr->next)
  2059.           if(address_is_us(addr, ps_global))
  2060.             to_us = '+';
  2061.  
  2062. #ifdef    LATER
  2063.     if(to_us == ' '){                /* look for reset-to */
  2064.     char    *fields[2], *resent, *p;
  2065.     ADDRESS *resent_addr = NULL;
  2066.  
  2067.     fields[0] = "Resent-To";
  2068.     fields[1] = NULL;
  2069.     resent = xmail_fetchheader_lines(stream,mn_m2raw(msgmap,msgno),fields);
  2070.     if(resent){
  2071.         if(p = strchr(resent, ':')){
  2072.         for(++p; *p && isspace((unsigned char)*p); p++)
  2073.           ;
  2074.  
  2075.         removing_trailing_white_space(p);
  2076.         rfc822_parse_adrlist(&resent_addr, p, ps_global->maildomain);
  2077.         for(addr = resent_addr; addr && to_us == ' '; addr=addr->next)
  2078.           if(address_is_us(addr, ps_global))
  2079.             to_us = '+';
  2080.  
  2081.         mail_free_address(&resent_addr);
  2082.         }
  2083.  
  2084.         fs_give((void **)&resent);
  2085.     }
  2086.     }
  2087. #endif
  2088.  
  2089.         sprintf(str, "%c %s", to_us, status_string(stream, cache));
  2090.         break;
  2091.  
  2092.           case iFStatus:
  2093.            {char new, answered, deleted, flagged;
  2094.  
  2095.         to_us = ' ';
  2096.         for(addr = envelope->to; addr && to_us == ' '; addr=addr->next)
  2097.           if(address_is_us(addr, ps_global))
  2098.             to_us = '+';
  2099.         new = answered = deleted = flagged = ' ';
  2100.         if(cache && !ps_global->nr_mode){
  2101.             if(!cache->seen &&
  2102.               (!stream
  2103.                || !IS_NEWS(stream)
  2104.                || (cache->recent&&F_ON(F_FAKE_NEW_IN_NEWS,ps_global))))
  2105.               new = 'N';
  2106.  
  2107.             if(cache->answered)
  2108.               answered = 'A';
  2109.  
  2110.             if(cache->deleted)
  2111.               deleted = 'D';
  2112.  
  2113.             if(cache->flagged)
  2114.               flagged = '*';
  2115.         }
  2116.  
  2117.         sprintf(str, "%c %c%c%c%c", to_us,
  2118.             flagged, new, answered, deleted);
  2119.            }
  2120.         break;
  2121.  
  2122.           case iMessNo:
  2123.         sprintf(str, "%ld", msgno);
  2124.         break;
  2125.  
  2126.           case iDate:
  2127.         parse_date(envelope->date, &d);
  2128.         sprintf(str, "%s %2d", month_abbrev(d.month), d.day);
  2129.         break;
  2130.  
  2131.           case iFromTo:
  2132.         addr = envelope->to ? envelope->to
  2133.                      : envelope->cc ? envelope->cc : NULL;
  2134.         field = envelope->to ? "To" : envelope->cc ? "Cc" : NULL;
  2135.         if(addr && (!envelope->from ||
  2136.                address_is_us(envelope->from, ps_global))){
  2137.  
  2138.             if(width > 4)
  2139.               set_index_addr(stream, mn_m2raw(msgmap, msgno),
  2140.                      field, addr, "To: ", width, str);
  2141.             else{
  2142.             strcpy(str, "To: ");
  2143.             str[width] = '\0';
  2144.             }
  2145.             
  2146.             break;
  2147.         }
  2148.  
  2149.         /*
  2150.          * Note: Newsgroups won't work over imap so we won't get
  2151.          * it in that case, and we'll use from.  We don't want to
  2152.          * waste an rtt getting it.
  2153.          */
  2154.         if(!addr && envelope->newsgroups
  2155.                && (!envelope->from
  2156.                || address_is_us(envelope->from, ps_global))){
  2157.             if(width > 4)
  2158.               sprintf(str, "To: %-*.*s", width-4, width-4,
  2159.                   envelope->newsgroups);
  2160.             else{
  2161.             strcpy(str, "To: ");
  2162.             str[width] = '\0';
  2163.             }
  2164.  
  2165.             break;
  2166.         }
  2167.         /* ELSE fall thru and act like a "From" */
  2168.  
  2169.           case iFrom:
  2170.         set_index_addr(stream, mn_m2raw(msgmap, msgno),
  2171.                    "From", envelope->from, NULL, width, str);
  2172.         break;
  2173.  
  2174.           case iTo:
  2175.         addr = envelope->to ? envelope->to
  2176.                      : envelope->cc ? envelope->cc : NULL;
  2177.         field = envelope->to ? "To" : envelope->cc ? "Cc" : NULL;
  2178.         if(!set_index_addr(stream, mn_m2raw(msgmap, msgno),
  2179.                    field, addr, NULL, width, str)
  2180.            && envelope->newsgroups)
  2181.           sprintf(str, "%-*.*s", width, width, envelope->newsgroups);
  2182.  
  2183.         break;
  2184.  
  2185.           case iSender:
  2186.         set_index_addr(stream, mn_m2raw(msgmap, msgno),
  2187.                    "Sender", envelope->sender, NULL, width, str);
  2188.         break;
  2189.  
  2190.           case iSize:
  2191.         if(!IS_NEWS(stream)){
  2192.             if(cache->rfc822_size < 100000){
  2193.             s_tmp = comatose(cache->rfc822_size);
  2194.             sprintf(str, "(%s)", s_tmp);
  2195.             }
  2196.             else if(cache->rfc822_size < 10000000){
  2197.             s_tmp = comatose(cache->rfc822_size/1000);
  2198.             sprintf(str, "(%sK)", s_tmp);
  2199.             }
  2200.             else
  2201.               strcpy(str, "(BIG!)");
  2202.         }
  2203.  
  2204.         break;
  2205.  
  2206.           case iDescripSize:
  2207.         if(!IS_NEWS(stream) && body){
  2208.           switch(body->type){
  2209.             case TYPETEXT:
  2210.               if(cache->rfc822_size < 6000)
  2211.             strcpy(str, "(short  )");
  2212.               else if(cache->rfc822_size < 25000)
  2213.             strcpy(str, "(medium )");
  2214.               else if(cache->rfc822_size < 100000)
  2215.             strcpy(str, "(long   )");
  2216.               else
  2217.             strcpy(str, "(huge   )");
  2218.  
  2219.               break;
  2220.  
  2221.             case TYPEMULTIPART:
  2222.               if(strucmp(body->subtype, "MIXED") == 0){
  2223.             switch(body->contents.part->body.type){
  2224.               case TYPETEXT:
  2225.                 if(body->contents.part->body.size.bytes < 6000)
  2226.                   strcpy(str, "(short+ )");
  2227.                 else if(body->contents.part->body.size.bytes < 25000)
  2228.                   strcpy(str, "(medium+)");
  2229.                 else if(body->contents.part->body.size.bytes < 100000)
  2230.                   strcpy(str, "(long+  )");
  2231.                 else
  2232.                   strcpy(str, "(huge+  )");
  2233.                 break;
  2234.  
  2235.               default:
  2236.                 strcpy(str, "(multi  )");
  2237.                 break;
  2238.             }
  2239.               }
  2240.               else if(strucmp(body->subtype, "DIGEST") == 0)
  2241.             strcpy(str, "(digest )");
  2242.               else if(strucmp(body->subtype, "ALTERNATIVE") == 0)
  2243.             strcpy(str, "(mul/alt)");
  2244.               else if(strucmp(body->subtype, "PARALLEL") == 0)
  2245.             strcpy(str, "(mul/par)");
  2246.               else
  2247.                 strcpy(str, "(multi  )");
  2248.  
  2249.               break;
  2250.  
  2251.             case TYPEMESSAGE:
  2252.               strcpy(str, "(message)");
  2253.               break;
  2254.  
  2255.             case TYPEAPPLICATION:
  2256.               strcpy(str, "(applica)");
  2257.               break;
  2258.  
  2259.             case TYPEAUDIO:
  2260.               strcpy(str, "(audio  )");
  2261.               break;
  2262.  
  2263.             case TYPEIMAGE:
  2264.               strcpy(str, "(image  )");
  2265.               break;
  2266.  
  2267.             case TYPEVIDEO:
  2268.               strcpy(str, "(video  )");
  2269.               break;
  2270.  
  2271.             default:
  2272.               strcpy(str, "(other  )");
  2273.               break;
  2274.           }
  2275.         }
  2276.         break;
  2277.  
  2278.           case iSubject:
  2279.         p = str;
  2280.         if(ps_global->nr_mode){
  2281.             str[0] = ' ';
  2282.             str[1] = '\0';
  2283.             p++;
  2284.             width--;
  2285.         }
  2286.  
  2287.         if(envelope->subject)
  2288.           istrncpy(p,
  2289.                (char*)rfc1522_decode((unsigned char *)tmp_20k_buf,
  2290.                          envelope->subject, NULL),
  2291.                width);
  2292.  
  2293.         break;
  2294.         }
  2295.     }
  2296.     }
  2297.  
  2298.     *buffer = '\0';
  2299.     p = buffer;
  2300.     /*--- Put them all together ---*/
  2301.     for(i = 0; cdesc[i].ctype != iNothing; i++){
  2302.     width = cdesc[i].width;
  2303.     if(width == 0)
  2304.       continue;
  2305.  
  2306.     /* space between columns */
  2307.     if(p > buffer){
  2308.         *p++ = ' ';
  2309.         *p = '\0';
  2310.     }
  2311.  
  2312.     if(cdesc[i].adjustment == Left)
  2313.       sprintf(p, "%-*.*s", width, width, cdesc[i].string);
  2314.     else
  2315.       sprintf(p, "%*.*s", width, width, cdesc[i].string);
  2316.     
  2317.     p += width;
  2318.     }
  2319.  
  2320.     /* Truncate it to be sure not too wide */
  2321.     buffer[min(ps_global->ttyo->screen_cols, i_cache_width())] = '\0';
  2322.     hline->id = line_hash(buffer);
  2323.     dprint(9, (debugfile, "Returning %p -> <%s (%d), %ld>\n",
  2324.            hline, buffer, strlen(buffer), hline->id));
  2325.     return(hline);
  2326. }
  2327.  
  2328.  
  2329. int
  2330. set_index_addr(stream, msgno, field, addr, prefix, width, s)
  2331.     MAILSTREAM *stream;
  2332.     long    msgno;
  2333.     char       *field;
  2334.     ADDRESS    *addr;
  2335.     char       *prefix;
  2336.     int        width;
  2337.     char       *s;
  2338. {
  2339.     ADDRESS *atmp;
  2340.  
  2341.     for(atmp = addr; stream && atmp; atmp = atmp->next)
  2342.       if(atmp->host && atmp->host[0] == '.'){
  2343.       char *p, *h, *fields[2];
  2344.  
  2345.       fields[0] = field;
  2346.       fields[1] = NULL;
  2347.       if(h = xmail_fetchheader_lines(stream, msgno, fields)){
  2348.           /* skip "field:" */
  2349.           for(p = h + strlen(field) + 1;
  2350.           *p && isspace((unsigned char)*p); p++)
  2351.         ;
  2352.  
  2353.           while(width--)
  2354.         if(*p == '\015' || *p == '\012')
  2355.           p++;                /* skip CR LF */
  2356.         else if(!*p)
  2357.           *s++ = ' ';
  2358.         else
  2359.           *s++ = *p++;
  2360.  
  2361.           *s = '\0';            /* tie off return string */
  2362.           fs_give((void **) &h);
  2363.           return(TRUE);
  2364.       }
  2365.       /* else fall thru and display what c-client gave us */
  2366.       }
  2367.  
  2368.     if(addr && !addr->next        /* only one address */
  2369.        && addr->host            /* not group syntax */
  2370.        && addr->personal){        /* there is a personal name */
  2371.     char *dummy = NULL;
  2372.     int   l;
  2373.  
  2374.     if(l = prefix ? strlen(prefix) : 0)
  2375.       strcpy(s, prefix);
  2376.  
  2377.     istrncpy(s + l,
  2378.          (char *) rfc1522_decode((unsigned char *)tmp_20k_buf,
  2379.                      addr->personal, &dummy),
  2380.          width - l);
  2381.     if(dummy)
  2382.       fs_give((void **)&dummy);
  2383.  
  2384.     return(TRUE);
  2385.     }
  2386.     else if(addr){
  2387.     char *a_string;
  2388.     int   l;
  2389.  
  2390.     a_string = addr_list_string(addr, NULL, 0);
  2391.     if(l = prefix ? strlen(prefix) : 0)
  2392.       strcpy(s, prefix);
  2393.  
  2394.     istrncpy(s + l, a_string, width - l);
  2395.  
  2396.     fs_give((void **)&a_string);
  2397.     return(TRUE);
  2398.     }
  2399.  
  2400.     return(FALSE);
  2401. }
  2402.  
  2403.  
  2404.  
  2405. long
  2406. line_hash(s)
  2407.      char *s;
  2408. {
  2409.     register long xsum = 0L;
  2410.  
  2411.     while(*s)
  2412.       xsum = ((((xsum << 4) & 0xffffffff) + (xsum >> 24)) & 0x0fffffff) + *s++;
  2413.  
  2414.     return(xsum ? xsum : 1L);
  2415. }
  2416.  
  2417.  
  2418. /*-----
  2419.   The following are used to report on sorting progress to the user
  2420.  
  2421. This code is not ideal as it varies depending on how the mail driver
  2422. performs with respect to fetching envelopes.  For some c-client drivers
  2423. the initial fetch of the envelope is expensive and it is cached after
  2424. that. For others every fetch is expensive.  The code here deals with
  2425. with the case where only the first fetch is expensive. It reports
  2426. progress based on how many messages have been fetched, assuming the sort
  2427. will complete very rapidly when all the messages have been fetched. 
  2428.  
  2429. There is a bitmap that keeps track of which messages have been
  2430. fetched that is used to keep an accurate count of the number of messages
  2431. that have been fetched. The sort algorithm fetches envelopes in a random
  2432. order and possibly many times. 
  2433.  
  2434. For c-client drivers for which every fetch envelope is expensive, this
  2435. algorithm will report that the sort is 100% complete once all the envelopes
  2436. have been fetched at least once.
  2437.   ----*/
  2438.  
  2439. #ifndef DOS
  2440. /*
  2441.  * Bitmap for keeping track of messages fetched so progress of
  2442.  * sort can be reported 
  2443.  */
  2444. static unsigned short *fetched_map;
  2445. static long           fetched_count;
  2446.  
  2447. #define  GET_FETCHED_MAP(x)  (fetched_map[(x-1)/16] & (0x1 << ((x-1) & 0xf)))
  2448. #define  SET_FETCHED_MAP(x)  (fetched_map[(x-1)/16] |= (0x1 << ((x-1) & 0xf)))
  2449.  
  2450.  
  2451. /*
  2452.  * Return value for use by progress bar.
  2453.  */
  2454. int
  2455. percent_sorted()
  2456. {
  2457.     return((fetched_count*100) / mn_get_total(ps_global->msgmap));
  2458. }
  2459. #endif    /* !DOS */
  2460.  
  2461.  
  2462. /*----------------------------------------------------------------------
  2463.   Compare function for sorting on subjects. Ignores case, space and "re:"
  2464.   ----*/
  2465. int
  2466. compare_subjects(a, b)
  2467.     const QSType *a, *b;
  2468. {
  2469.     char *suba, *subb;
  2470.     long *mess_a = (long *)a, *mess_b = (long *)b;
  2471.     int   diff, res;
  2472.     long  mdiff;
  2473.  
  2474.     if(ps_global->intr_pending)
  2475.       longjmp(jump_past_qsort, 1);
  2476.  
  2477.     suba = get_sub(*mess_a);
  2478.     subb = get_sub(*mess_b);
  2479.  
  2480.     diff = strucmp(suba, subb);
  2481.  
  2482.     if(diff == 0)
  2483.       mdiff = *mess_a - *mess_b;
  2484.  
  2485.     /* convert to int */
  2486.     res = diff != 0 ? diff :
  2487.            mdiff != 0L ? (mdiff > 0L ? 1 : -1) : 0;
  2488.     return(mn_get_revsort(ps_global->msgmap) ? -res : res);
  2489. }
  2490.  
  2491.  
  2492. /*----------------------------------------------------------------------
  2493.    Get the subject of a message suitable for sorting.  This removes
  2494.  all re[*]: or [ from the beginning of a message.
  2495.  ----*/
  2496. char *
  2497. get_sub(mess)
  2498.     long mess;
  2499. {
  2500.     ENVELOPE *e;
  2501.     char *subj, *s, *s3, *ss, *dummy = NULL;
  2502.     int   l;
  2503.  
  2504.     if(!(ss = subject_cache_ent(mess))){
  2505.     e = mail_fetchstructure(ps_global->mail_stream, mess, NULL);
  2506.     if(e && e->subject) {
  2507.         /* ---- Deal with any necessary RFC 1522 decoding ----*/
  2508.         subj = (char *)rfc1522_decode((unsigned char *)tmp_20k_buf,
  2509.                       e->subject, &dummy);
  2510.         if(dummy)
  2511.           fs_give((void **)&dummy);
  2512.  
  2513.         /* ---- Clean junk off the front of the subject ----*/
  2514.         for(s = subj; *s && (isspace((unsigned char)*s) || *s == '['); s++)
  2515.           /* do nothing */;
  2516.  
  2517.         if((*s == 'R' || *s == 'r') && (*(s+1) == 'E' || *(s+1) == 'e')
  2518.            && (*(s+2) == ':' || *(s+2) == '[')){
  2519.         s += 3;
  2520.         if(*(s+2) == '['){
  2521.             while(*s && *s != ':')
  2522.               s++;
  2523.  
  2524.             s++;
  2525.         }
  2526.         }
  2527.  
  2528.         while(*s && (isspace((unsigned char)*s) || *s == '['))
  2529.           s++;
  2530.  
  2531.         if(*(ss = subject_cache_add(mess, s))){
  2532.         /*----- Now, truncate junk off the back end of the subject---*/
  2533.         for(s3 = NULL, s = ss; *s; s++)    /* blast ws and ']' */
  2534.           s3 = (!isspace((unsigned char)*s) && *s != ']')
  2535.                           ? NULL : (!s3) ? s : s3;
  2536.  
  2537.         if(s3)
  2538.           *s3 = '\0';
  2539.  
  2540.         if((l=(s3 ? s3 : s)-ss) > 5 && !strucmp(ss+l-5,"(fwd)"))
  2541.           ss[l-5] = '\0';
  2542.  
  2543.         for(s3 = NULL, s = ss; *s; s++)    /* blast ws and ']' */
  2544.           s3 = (!isspace((unsigned char)*s) && *s != ']')
  2545.                           ? NULL : (!s3) ? s : s3;
  2546.  
  2547.         if(s3)
  2548.           *s3 = '\0';
  2549.         }
  2550.     }
  2551.     else
  2552.       ss = subject_cache_add(mess, "");
  2553.  
  2554.     dprint(9, (debugfile, "SUB-GET-%s-GET-SUB\n", ss));
  2555.     }
  2556.     else{
  2557.     dprint(9, (debugfile, "SUB-HIT-%s-HIT-SUB\n", ss));
  2558.     }
  2559.  
  2560. #ifndef DOS
  2561.     if(fetched_map != NULL && !GET_FETCHED_MAP(mess)) {
  2562.         SET_FETCHED_MAP(mess);
  2563.         fetched_count++;
  2564.     }
  2565. #endif
  2566.  
  2567.     ALARM_BLIP();
  2568.  
  2569.     return(ss);
  2570. }
  2571.  
  2572.  
  2573. /*----------------------------------------------------------------------
  2574.    Compare the From: fields for sorting. Ignore case.  Only consider the
  2575.    mailbox portion of the address (part left of @).
  2576.    ----*/
  2577. int 
  2578. compare_from(a, b)
  2579.     const QSType *a, *b;
  2580. {
  2581.     long     *mess_a = (long *)a, *mess_b = (long *)b;
  2582.     ENVELOPE *e;
  2583.     char      froma[200], fromb[200];
  2584.     int       diff, res;
  2585.  
  2586.     if(ps_global->intr_pending)
  2587.       longjmp(jump_past_qsort, 1);
  2588.  
  2589.     e = mail_fetchstructure(ps_global->mail_stream, *mess_a, NULL);
  2590.     if(e == NULL || e->from == NULL || e->from->mailbox == NULL)  
  2591.       froma[0] = '\0';
  2592.     else
  2593.       strncpy(froma, e->from->mailbox, sizeof(froma) - 1);
  2594.  
  2595.     froma[sizeof(froma) - 1] = '\0';
  2596.  
  2597.     e = mail_fetchstructure(ps_global->mail_stream, *mess_b, NULL);
  2598.     if(e == NULL || e->from == NULL || e->from->mailbox == NULL)  
  2599.       fromb[0] = '\0';
  2600.     else
  2601.       strncpy(fromb, e->from->mailbox, sizeof(fromb) - 1);
  2602.  
  2603.     fromb[sizeof(fromb) - 1] = '\0';
  2604.  
  2605. #ifndef DOS
  2606.     if(!GET_FETCHED_MAP(*mess_a)){
  2607.         SET_FETCHED_MAP(*mess_a);
  2608.         fetched_count++;
  2609.     }
  2610.  
  2611.     if(!GET_FETCHED_MAP(*mess_b)){
  2612.         SET_FETCHED_MAP(*mess_b);
  2613.         fetched_count++;
  2614.     }
  2615. #endif
  2616.  
  2617.     ALARM_BLIP();
  2618.  
  2619.     diff = strucmp(froma, fromb);
  2620.     if(diff == 0){
  2621.     long mdiff;
  2622.  
  2623.         mdiff = *mess_a - *mess_b;  /* arrival order */
  2624.     /* convert to int */
  2625.     res = mdiff != 0L ? (mdiff > 0L ? 1 : -1) : 0;
  2626.     }
  2627.     else
  2628.       res = diff;
  2629.     
  2630.     return(mn_get_revsort(ps_global->msgmap) ? -res : res);
  2631.  
  2632.  
  2633. /*----------------------------------------------------------------------
  2634.    Compare the To: fields for sorting. Ignore case. Use 1st to.
  2635.    ----*/
  2636. int 
  2637. compare_to(a, b)
  2638.     const QSType *a, *b;
  2639. {
  2640.     long     *mess_a = (long *)a, *mess_b = (long *)b;
  2641.     ENVELOPE *e;
  2642.     char      toa[200], tob[200];
  2643.     int       diff, res;
  2644.  
  2645.     if(ps_global->intr_pending)
  2646.       longjmp(jump_past_qsort, 1);
  2647.  
  2648.     e = mail_fetchstructure(ps_global->mail_stream, *mess_a, NULL);
  2649.     if(e == NULL || e->to == NULL || e->to->mailbox == NULL)  
  2650.       toa[0] = '\0';
  2651.     else
  2652.       strncpy(toa, e->to->mailbox, sizeof(toa) - 1);
  2653.  
  2654.     toa[sizeof(toa) - 1] = '\0';
  2655.  
  2656.     e = mail_fetchstructure(ps_global->mail_stream, *mess_b, NULL);
  2657.     if(e == NULL || e->to == NULL || e->to->mailbox == NULL)  
  2658.       tob[0] = '\0';
  2659.     else
  2660.       strncpy(tob, e->to->mailbox, sizeof(tob) - 1);
  2661.  
  2662.     tob[sizeof(tob) - 1] = '\0';
  2663.  
  2664. #ifndef DOS
  2665.     if(!GET_FETCHED_MAP(*mess_a)){
  2666.         SET_FETCHED_MAP(*mess_a);
  2667.         fetched_count++;
  2668.     }
  2669.  
  2670.     if(!GET_FETCHED_MAP(*mess_b)){
  2671.         SET_FETCHED_MAP(*mess_b);
  2672.         fetched_count++;
  2673.     }
  2674. #endif
  2675.  
  2676.     ALARM_BLIP();
  2677.  
  2678.     diff = strucmp(toa, tob);
  2679.     if(diff == 0){
  2680.     long mdiff;
  2681.  
  2682.         mdiff = *mess_a - *mess_b;  /* arrival order */
  2683.     /* convert to int */
  2684.     res = mdiff != 0L ? (mdiff > 0L ? 1 : -1) : 0;
  2685.     }
  2686.     else
  2687.       res = diff;
  2688.     
  2689.     return(mn_get_revsort(ps_global->msgmap) ? -res : res);
  2690.  
  2691.  
  2692. /*----------------------------------------------------------------------
  2693.    Compare the Cc: fields for sorting. Ignore case. Use 1st cc.
  2694.    ----*/
  2695. int 
  2696. compare_cc(a, b)
  2697.     const QSType *a, *b;
  2698. {
  2699.     long     *mess_a = (long *)a, *mess_b = (long *)b;
  2700.     ENVELOPE *e;
  2701.     char      cca[200], ccb[200];
  2702.     int       diff, res;
  2703.  
  2704.     if(ps_global->intr_pending)
  2705.       longjmp(jump_past_qsort, 1);
  2706.  
  2707.     e = mail_fetchstructure(ps_global->mail_stream, *mess_a, NULL);
  2708.     if(e == NULL || e->cc == NULL || e->cc->mailbox == NULL)  
  2709.       cca[0] = '\0';
  2710.     else
  2711.       strncpy(cca, e->cc->mailbox, sizeof(cca) - 1);
  2712.  
  2713.     cca[sizeof(cca) - 1] = '\0';
  2714.  
  2715.     e = mail_fetchstructure(ps_global->mail_stream, *mess_b, NULL);
  2716.     if(e == NULL || e->cc == NULL || e->cc->mailbox == NULL)  
  2717.       ccb[0] = '\0';
  2718.     else
  2719.       strncpy(ccb, e->cc->mailbox, sizeof(ccb) - 1);
  2720.  
  2721.     ccb[sizeof(ccb) - 1] = '\0';
  2722.  
  2723. #ifndef DOS
  2724.     if(!GET_FETCHED_MAP(*mess_a)){
  2725.         SET_FETCHED_MAP(*mess_a);
  2726.         fetched_count++;
  2727.     }
  2728.  
  2729.     if(!GET_FETCHED_MAP(*mess_b)){
  2730.         SET_FETCHED_MAP(*mess_b);
  2731.         fetched_count++;
  2732.     }
  2733. #endif
  2734.  
  2735.     ALARM_BLIP();
  2736.  
  2737.     diff = strucmp(cca, ccb);
  2738.     if(diff == 0){
  2739.     long mdiff;
  2740.  
  2741.         mdiff = *mess_a - *mess_b;  /* arrival order */
  2742.     /* convert to int */
  2743.     res = mdiff != 0L ? (mdiff > 0L ? 1 : -1) : 0;
  2744.     }
  2745.     else
  2746.       res = diff;
  2747.     
  2748.     return(mn_get_revsort(ps_global->msgmap) ? -res : res);
  2749.  
  2750.  
  2751. /*----------------------------------------------------------------------
  2752.    Compare dates (not arrival times, but time from Date header)
  2753.   ----*/
  2754. int
  2755. compare_message_dates(a, b)
  2756.     const QSType *a, *b;
  2757. {
  2758.     long        *mess_a = (long *)a, *mess_b = (long *)b;
  2759.     int          diff, res;
  2760.     long         mdiff;
  2761.     ENVELOPE    *e;
  2762.     MESSAGECACHE mc_a, mc_b;
  2763.  
  2764.     if(ps_global->intr_pending)
  2765.       longjmp(jump_past_qsort, 1);
  2766.  
  2767.     mdiff = *mess_a - *mess_b;
  2768.     res = mdiff != 0L ? (mdiff > 0L ? 1 : -1) : 0;
  2769.  
  2770.     mc_a.minutes   = mc_b.minutes = 0;        /* init interesting bits */
  2771.     mc_a.hours       = mc_b.hours = 0;
  2772.     mc_a.day       = mc_b.day = 0;
  2773.     mc_a.month       = mc_b.month = 0;
  2774.     mc_a.year       = mc_b.year = 0;
  2775.     mc_a.zhours       = mc_b.zhours = 0;
  2776.     mc_a.zminutes  = mc_b.zminutes = 0;
  2777.     mc_a.zoccident = mc_b.zoccident = 0;
  2778.  
  2779.     e = mail_fetchstructure(ps_global->mail_stream, *mess_a, NULL);
  2780.     mc_a.valid = (e && e->date && mail_parse_date(&mc_a, e->date));
  2781.  
  2782.     e = mail_fetchstructure(ps_global->mail_stream, *mess_b, NULL);
  2783.     mc_b.valid = (e && e->date && mail_parse_date(&mc_b, e->date));
  2784.  
  2785.     if(!mc_a.valid)
  2786.       return((mn_get_revsort(ps_global->msgmap) ? -1 : 1)
  2787.           * (mc_b.valid ? -1 : res));
  2788.     else if(!mc_b.valid)
  2789.       return(mn_get_revsort(ps_global->msgmap) ? -1 : 1);
  2790.  
  2791.     diff = compare_dates(&mc_a, &mc_b);
  2792.  
  2793. #ifndef DOS
  2794.     if(!GET_FETCHED_MAP(*mess_a)){
  2795.         SET_FETCHED_MAP(*mess_a);
  2796.         fetched_count++;
  2797.     }
  2798.  
  2799.     if(!GET_FETCHED_MAP(*mess_b)){
  2800.         SET_FETCHED_MAP(*mess_b);
  2801.         fetched_count++;
  2802.     }
  2803. #endif
  2804.  
  2805.     ALARM_BLIP();
  2806.     
  2807.     res = diff != 0 ? diff : res;
  2808.     return(mn_get_revsort(ps_global->msgmap) ? -res : res);
  2809. }
  2810.  
  2811.     
  2812.  
  2813. /*----------------------------------------------------------------------
  2814.   Compare size of messages for sorting
  2815.  ----*/
  2816. int
  2817. compare_size(a, b)
  2818.     const QSType *a, *b;
  2819. {
  2820.     long      *mess_a = (long *)a, *mess_b = (long *)b;
  2821.     long      size_a, size_b, sdiff, mdiff;
  2822.     MESSAGECACHE *mc;
  2823.     int          res;
  2824.  
  2825.     if(ps_global->intr_pending)
  2826.       longjmp(jump_past_qsort, 1);
  2827.  
  2828.     mail_fetchstructure(ps_global->mail_stream, *mess_a, NULL);
  2829.     mc = mail_elt(ps_global->mail_stream, *mess_a);
  2830.     size_a = mc != NULL ? mc->rfc822_size : -1L;
  2831.  
  2832.     mail_fetchstructure(ps_global->mail_stream, *mess_b, NULL);
  2833.     mc = mail_elt(ps_global->mail_stream, *mess_b);
  2834.     size_b = mc != NULL ? mc->rfc822_size : -1L;
  2835.  
  2836. #ifndef DOS
  2837.     if(!GET_FETCHED_MAP(*mess_a)){
  2838.         SET_FETCHED_MAP(*mess_a);
  2839.         fetched_count++;
  2840.     }
  2841.  
  2842.     if(!GET_FETCHED_MAP(*mess_b)){
  2843.         SET_FETCHED_MAP(*mess_b);
  2844.         fetched_count++;
  2845.     }
  2846. #endif    
  2847.  
  2848.     ALARM_BLIP();
  2849.  
  2850.     sdiff = size_a - size_b;
  2851.     if(sdiff == 0L)
  2852.       mdiff = *mess_a - *mess_b;
  2853.  
  2854.     /* convert to int */
  2855.     res = sdiff != 0L ? (sdiff > 0L ? 1 : -1) :
  2856.            mdiff != 0L ? (mdiff > 0L ? 1 : -1) : 0;
  2857.     return(mn_get_revsort(ps_global->msgmap) ? -res : res);
  2858. }
  2859.  
  2860.  
  2861. /*----------------------------------------------------------------------
  2862.   Compare raw message numbers 
  2863.  ----*/
  2864. int
  2865. compare_arrival(a, b)
  2866.     const QSType *a, *b;
  2867. {
  2868.     long *mess_a = (long *)a, *mess_b = (long *)b, mdiff;
  2869.     int   res;
  2870.  
  2871.     res = (mdiff = *mess_a - *mess_b) ? (mdiff > 0L ? 1 : -1) : 0;
  2872.     return(mn_get_revsort(ps_global->msgmap) ? -res : res);
  2873. }
  2874.  
  2875.  
  2876. #if    defined(DOS) && !defined(_WINDOWS)
  2877. static FILE *second_sort_file;    /* Can't afford an array with DOS */
  2878. static char *second_sort_file_name = NULL;
  2879. #else
  2880. static long *second_sort;    /* Array of message numbers */
  2881. #endif
  2882.  
  2883. /*----------------------------------------------------------------------
  2884.    Each message is in a subject group.  That group is indexed by the
  2885.    Date of its oldest member.  Sort first by that date, and within a
  2886.    single subject group, sort by Date of each message.
  2887.   ----*/
  2888. int
  2889. compare_subject_2(a, b)
  2890.     const QSType *a, *b;
  2891. {
  2892.     long       *mess_a = (long *)a, *mess_b = (long *)b;
  2893.     long        a1, b1;
  2894.     long        diff;
  2895.     int         res;
  2896.  
  2897.     if(ps_global->intr_pending)
  2898.       longjmp(jump_past_qsort, 1);
  2899.  
  2900. #if    defined(DOS) && !defined(_WINDOWS)
  2901.     /* There ought to be a better way than seeking all over 
  2902.        the disk, but too lazy at the moment */
  2903.     fseek(second_sort_file, *mess_a * sizeof(long), 0);
  2904.     fread((void *)&a1, sizeof(a1), (size_t)1, second_sort_file);
  2905.     fseek(second_sort_file, *mess_b * sizeof(long), 0);
  2906.     fread((void *)&b1, sizeof(b1), (size_t)1, second_sort_file);
  2907. #else
  2908.     a1 = second_sort[*mess_a];
  2909.     b1 = second_sort[*mess_b];
  2910. #endif
  2911.     
  2912.     diff = a1 - b1;
  2913.  
  2914.     /*
  2915.      * Note, secondary sort is by Date instead of by arrival time as
  2916.      * with most of the other compare functions.
  2917.      */
  2918.     res = diff != 0L ? (diff > 0L ? 1 : -1) : compare_message_dates(a, b);
  2919.  
  2920.     return((mn_get_revsort(ps_global->msgmap) && diff != 0L) ? -res : res);
  2921. }
  2922.  
  2923.  
  2924.     
  2925. /*----------------------------------------------------------------------
  2926.      Sort messages on subject, grouping subjects by date of oldest member
  2927.  
  2928. Args: sort -- The resulting sorted array of message numbers
  2929.       max_msgno -- The number of messages in this view
  2930.       total_max -- The largest possible message number
  2931.  
  2932. This is the OrderedSubjectSort style of subject sort akin to message threading
  2933. based on subject.  All the subjects are grouped together and then the 
  2934. groups are ordered based on the oldest message in each group. 
  2935.  
  2936. Here the groups of subjects are sorted.  Before calling this function,
  2937. the messages have been sorted by subject.
  2938.  
  2939. The second array is indexed by raw message numbers and contains the
  2940. the date (in seconds) of the oldest message in the same subject group.
  2941.  
  2942. This code could be used to calculate information for true
  2943. message threading.  For example, a data structure that had one entry
  2944. per unique subject.  The data structure would probably only need to contain
  2945. the *sorted* message number of the first message in the thread and the 
  2946. number of messages in the thread. That would be assuming that the sort
  2947. array is sorted in the appropriate order. 
  2948.  
  2949. On DOS where memory is expensive the secondary array is a disk file instead
  2950. of being in memory. (Though for many formats sorting on DOS will be 
  2951. extremely painful until some local caching of sort terms is invented.)
  2952.  ----*/
  2953. void
  2954. second_subject_sort(sort, max_msgno, total_max)
  2955.     long *sort, max_msgno, total_max;
  2956. {
  2957.     char  *sub_group, *sub;
  2958.     long   start_sub_group, m, s, t, min_sub_group_date;
  2959.     long   for_zero_dates = 0L;
  2960.     int    check_again;
  2961.  
  2962.     if(max_msgno < 1 || total_max < 1)
  2963.       return; 
  2964.  
  2965. #if    defined(DOS) && !defined(_WINDOWS)
  2966.     second_sort_file_name = temp_nam(NULL, "sf");
  2967.     second_sort_file      = fopen(second_sort_file_name, "wb+");
  2968. #else
  2969.     second_sort      = (long *)fs_get(sizeof(long) * (size_t)(total_max + 1));
  2970. #endif
  2971.  
  2972.     /* init for loop */
  2973.     sub_group          = get_sub(sort[1]);
  2974.     start_sub_group    = 1;
  2975.     min_sub_group_date = seconds_since_epoch(sort[1]);
  2976.     /*
  2977.      * This is so that unparseable dates won't all be the same.  That would
  2978.      * make it look like they're all in the same subject group.
  2979.      */
  2980.     if(min_sub_group_date == 0){
  2981.     for_zero_dates += 10;
  2982.     min_sub_group_date = for_zero_dates;
  2983.     }
  2984.  
  2985.     /*
  2986.      * Create the second array of subject groups.  The sort array had to be
  2987.      * sorted by subject before this function was called, so that each
  2988.      * subject group would be contiguous in the array.  (Didn't actually have
  2989.      * to be sorted, just contiguous subject groups in any order.)
  2990.      */
  2991.     for(s = 2; s <= max_msgno; s++){
  2992.     sub = get_sub(sort[s]);
  2993.         if(strucmp(sub_group, sub)){ /* new subject */
  2994.         /* record the previous group */
  2995. #if    !defined(DOS) || defined(_WINDOWS)
  2996.         /*
  2997.          * If two different groups both have the same min_sub_group_date,
  2998.          * then they'll get mushed together when we sort.  We want to make
  2999.          * sure that doesn't happen, so we need to check if this date
  3000.          * is already recorded somewhere in second_sort and change it
  3001.          * if it is.
  3002.          *
  3003.          * (Too expensive for DOS, count on it being very rare.)
  3004.          */
  3005.         check_again = 1;
  3006.         while(check_again){
  3007.         check_again = 0;
  3008.         for(m = 1; m < start_sub_group; m++){
  3009.             if(min_sub_group_date == second_sort[sort[m]]){
  3010.             min_sub_group_date++;
  3011.             check_again = 1;
  3012.             break;
  3013.             }
  3014.         }
  3015.         }
  3016. #endif
  3017.             for(t = start_sub_group; t < s; t++){
  3018. #if    defined(DOS) && !defined(_WINDOWS)
  3019.                 fseek(second_sort_file, sort[t] * sizeof(long), 0);
  3020.                 fwrite((void *)&min_sub_group_date,
  3021.                        sizeof(min_sub_group_date),
  3022.                        (size_t)1,  second_sort_file);
  3023. #else
  3024.         second_sort[sort[t]] = min_sub_group_date;
  3025. #endif                
  3026.         }
  3027.  
  3028.         /* reset for next subject */
  3029.             start_sub_group     = s;
  3030.             sub_group           = sub;
  3031.         }
  3032.  
  3033.     if(s > start_sub_group)
  3034.       min_sub_group_date =
  3035.         min(min_sub_group_date, seconds_since_epoch(sort[s]));
  3036.     else /* new subject */
  3037.       min_sub_group_date = seconds_since_epoch(sort[s]);
  3038.  
  3039.     if(min_sub_group_date == 0){
  3040.         for_zero_dates += 10;
  3041.         min_sub_group_date = for_zero_dates;
  3042.     }
  3043.     }
  3044.  
  3045.     /* record final group */
  3046. #if    !defined(DOS) || defined(_WINDOWS)
  3047.     check_again = 1;
  3048.     while(check_again){
  3049.         check_again = 0;
  3050.         for(m = 1; m < start_sub_group; m++){
  3051.         if(min_sub_group_date == second_sort[sort[m]]){
  3052.             min_sub_group_date++;
  3053.             check_again = 1;
  3054.             break;
  3055.         }
  3056.         }
  3057.     }
  3058. #endif
  3059.  
  3060.     for(t = start_sub_group; t < s; t++){
  3061. #if    defined(DOS) && !defined(_WINDOWS)
  3062.         fseek(second_sort_file, sort[t] * sizeof(long), 0);
  3063.         fwrite((void *)&min_sub_group_date, sizeof(min_sub_group_date),
  3064.                        (size_t)1,  second_sort_file);
  3065. #else
  3066.     second_sort[sort[t]] = min_sub_group_date;
  3067. #endif
  3068.     }
  3069.  
  3070. #ifdef DEBUG
  3071.     for(t = 1; t <= max_msgno; t++) 
  3072.       dprint(9, (debugfile, "Second_sort[%3ld] is %3ld\n", t, second_sort[t]));
  3073. #endif
  3074.  
  3075.     /* Actually perform the sort */
  3076.     qsort(sort+1, (size_t)max_msgno, sizeof(long), compare_subject_2);
  3077.  
  3078.     /* Clean up */
  3079. #if    defined(DOS) && !defined(_WINDOWS)
  3080.     fclose(second_sort_file);
  3081.     unlink(second_sort_file_name);
  3082.     fs_give((void **)&second_sort_file_name);
  3083. #else    
  3084.     fs_give((void **)&second_sort);
  3085. #endif
  3086. }
  3087.  
  3088.  
  3089. /*
  3090.  * This is just used to help with the OrderedSubjSort.
  3091.  * The argument is a raw message number and the return value is the number
  3092.  * of seconds between the start of the BASEYEAR (1969) and the date in the
  3093.  * Date header.  Returns 0 if date can't be parsed or no date.
  3094.  */
  3095. long
  3096. seconds_since_epoch(msgno)
  3097. long msgno;
  3098. {
  3099.     ENVELOPE    *e;
  3100.     MESSAGECACHE mc;
  3101.     long seconds = 0L;
  3102.     int y, m;
  3103.  
  3104. #define MINUTE       (60L)
  3105. #define HOUR         (60L * MINUTE)
  3106. #define DAY          (24L * HOUR)
  3107. #define MONTH(mo,yr) ((long)days_in_month(mo,yr) * DAY)
  3108. #define YEAR(yr)     (((!((yr)%4L) && ((yr)%100L)) ? 366L : 365L) * DAY)
  3109.  
  3110.     e = mail_fetchstructure(ps_global->mail_stream, msgno, NULL);
  3111.     if(e == NULL || e->date == NULL)
  3112.       return(0L);
  3113.  
  3114.     if(!mail_parse_date(&mc, e->date))
  3115.       return(0L);
  3116.  
  3117.     convert_to_gmt(&mc);
  3118.  
  3119.     for(y = 0; (unsigned)y < mc.year; y++)
  3120.       seconds += YEAR(y+BASEYEAR);
  3121.  
  3122.     for(m = 1; (unsigned)m < mc.month; m++)
  3123.       seconds += MONTH(m, mc.year + BASEYEAR);
  3124.     
  3125.     seconds +=
  3126.     ((mc.day-1) * DAY + mc.hours * HOUR + mc.minutes * MINUTE + mc.seconds);
  3127.  
  3128.     return(seconds);
  3129. }
  3130.  
  3131.  
  3132. /*----------------------------------------------------------------------
  3133.     Sort the current folder into the order set in the msgmap
  3134.  
  3135. Args: defer_on_intr   -- If we get interrupted before finishing, set the
  3136.               deferred variable.
  3137.       default_so      -- If we get interrupted before finishing, set the
  3138.               sort order to default_so.
  3139.       default_reverse -- If we get interrupted before finishing, set the
  3140.               revsort to default_reverse.
  3141.     
  3142.     The idea of the deferred sort is to let the user interrupt a long sort
  3143.     and have a chance to do a different command, such as a sort by arrival
  3144.     or a Goto.  The next newmail call will increment the deferred variable,
  3145.     then the user may do a command, then the newmail call after that
  3146.     causes the sort to happen if it is still needed.
  3147.   ----*/
  3148. void
  3149. sort_current_folder(defer_on_intr, default_so, default_reverse)
  3150.     int defer_on_intr;
  3151.     SortOrder default_so;
  3152.     int default_reverse;
  3153. {
  3154.     long        i, *sort, *saved_sort, total = mn_get_total(ps_global->msgmap);
  3155.     MSGNO_S    *sortmap = NULL;
  3156.     SortOrder   so;
  3157.     char        sort_msg[101];
  3158.     int         skip_qsort = 0, we_cancel = 0, rev, is_default_order;
  3159.  
  3160.     so = mn_get_sort(ps_global->msgmap);    /* current sort order */
  3161.     rev = mn_get_revsort(ps_global->msgmap);    /* currently reverse? */
  3162.     /*
  3163.      * If is_default_order, that means we aren't changing the sort order,
  3164.      * we're just sorting because we may have gotten new mail or unexcluded
  3165.      * some mail that was hidden.
  3166.      */
  3167.     is_default_order = (so == default_so && rev == default_reverse);
  3168.     if(!ps_global->msgmap || !(sort = ps_global->msgmap->sort))
  3169.       return;
  3170.  
  3171.     dprint(2, (debugfile, "Sorting by %s%s\n", sort_name(so),
  3172.            mn_get_revsort(ps_global->msgmap) ? "/reverse" : ""));
  3173.  
  3174.     /*
  3175.      * translate the selected numbers into an array of raw numbers
  3176.      * temporarily, then translate it back after the sort so the
  3177.      * same physical messages are selected...
  3178.      */
  3179.     mn_init(&sortmap, 0L);
  3180.     mn_set_cur(sortmap, mn_m2raw(ps_global->msgmap,
  3181.                  mn_first_cur(ps_global->msgmap)));
  3182.     while((i = mn_next_cur(ps_global->msgmap)) > 0L)
  3183.       mn_add_cur(sortmap, mn_m2raw(ps_global->msgmap, i));
  3184.  
  3185.     if(so == SortArrival){
  3186.     /*
  3187.      * BEWARE: "exclusion" may leave holes in the unsorted sort order
  3188.      * so we have to do a real sort if that is the case.
  3189.      */
  3190.     if(any_lflagged(ps_global->msgmap, MN_EXLD))
  3191.       qsort(sort+1, (size_t) total, sizeof(long), compare_arrival);
  3192.     else
  3193.       for(i = 1L; i <= total; i++)
  3194.         sort[i] = mn_get_revsort(ps_global->msgmap) ? (total + 1 - i) : i;
  3195.     }
  3196.     else{
  3197.         if(so == SortSubject || so == SortSubject2)    /* nmsgs cuz MN_EXLD */
  3198.       init_subject_cache(ps_global->mail_stream->nmsgs);
  3199.  
  3200.         /*========= Sorting ================================================*/
  3201. #ifndef DOS
  3202.         /*--- fetched map for keep track of progress of sort ----*/
  3203.         fetched_count = 0;
  3204.         fetched_map   = (unsigned short *)fs_get(
  3205.                          (ps_global->mail_stream->nmsgs/16 + 1)
  3206.                                               * sizeof(unsigned short));
  3207.     memset((void *)fetched_map, 0,
  3208.         (ps_global->mail_stream->nmsgs/16 + 1) * sizeof(unsigned short));
  3209. #endif
  3210.  
  3211.  
  3212. #ifdef DOS
  3213.     sprintf(sort_msg, "Sorting \"%.90s\"", ps_global->cur_folder);
  3214.     we_cancel = busy_alarm(1, sort_msg, NULL, 1);
  3215. #else
  3216.     sprintf(sort_msg, "Sorting \"%.90s\"", ps_global->cur_folder);
  3217.     we_cancel = busy_alarm(1, sort_msg, percent_sorted, 1);
  3218.  
  3219.     /*
  3220.      * Interruptible sorting is only available under unix where we
  3221.      * can rely on the tty driver to deliver the SIGINT.  If we
  3222.      * can safely work out a similar scheme under windows/max, this
  3223.      * #ifdef will have to get rearranged...
  3224.      */
  3225.     /* save array in case of interrupt */
  3226.     saved_sort
  3227.         = (long *)fs_get(ps_global->msgmap->sort_size * sizeof(long));
  3228.     for(i = 1L; i <= total; i++)
  3229.       saved_sort[i] = sort[i];
  3230.  
  3231.     if(setjmp(jump_past_qsort)){
  3232.         intr_handling_off();
  3233.         if(we_cancel)
  3234.           cancel_busy_alarm(-1);
  3235.  
  3236.         we_cancel = 0;
  3237.         skip_qsort = 1;
  3238.         /* restore default SortOrder */
  3239.         mn_set_sort(ps_global->msgmap, default_so);
  3240.         mn_set_revsort(ps_global->msgmap, default_reverse);
  3241.         /* restore sort array */
  3242.         for(i = 1L; i <= total; i++)
  3243.           sort[i] = saved_sort[i];
  3244.  
  3245.         fs_give((void **)&saved_sort); 
  3246.     }
  3247. #endif
  3248.  
  3249.     if(!skip_qsort){
  3250. #ifndef    DOS
  3251.         intr_handling_on();
  3252. #endif
  3253.  
  3254.         qsort(sort + 1, (size_t) total, sizeof(long),
  3255.           so == SortSubject  ? compare_subjects :
  3256.            so == SortFrom     ? compare_from :
  3257.             so == SortTo       ? compare_to :
  3258.              so == SortCc       ? compare_cc :
  3259.               so == SortDate     ? compare_message_dates :
  3260.                so == SortSubject2 ? compare_subjects:
  3261.                                          compare_size);
  3262.  
  3263.         /*---- special case -- reorder the groups of subjects ------*/
  3264.         if(so == SortSubject2) 
  3265.           second_subject_sort(sort, total, ps_global->mail_stream->nmsgs);
  3266. #ifndef    DOS
  3267.         intr_handling_off();
  3268.         fs_give((void **)&saved_sort); 
  3269. #endif
  3270.     }
  3271.  
  3272.     if(we_cancel)
  3273.       cancel_busy_alarm(1);
  3274.  
  3275.     if(skip_qsort)
  3276.       q_status_message3(SM_ORDER, 3, 3, "Sort cancelled%s%s%s",
  3277.           is_default_order ? "" : "; now sorting by ",
  3278.           is_default_order ? "" : sort_name(default_so),
  3279.           is_default_order ? "" : default_reverse ? "/reverse" : "");
  3280.  
  3281. #ifndef DOS
  3282.         fs_give((void **)&fetched_map); 
  3283. #endif
  3284.     clear_subject_cache();
  3285.     }
  3286.  
  3287.     if(skip_qsort)
  3288.       ps_global->unsorted_newmail = 1;
  3289.     else{
  3290.     ps_global->sort_is_deferred = 0;
  3291.     ps_global->unsorted_newmail = 0;
  3292.     }
  3293.  
  3294.     /*
  3295.      * restore the selected array of message numbers.  No expunge could
  3296.      * have happened yet, so we don't worry about raw2m returning 0...
  3297.      */
  3298.     mn_reset_cur(ps_global->msgmap, mn_raw2m(ps_global->msgmap,
  3299.                          mn_first_cur(sortmap)));
  3300.     while((i = mn_next_cur(sortmap)) > 0L)
  3301.       mn_add_cur(ps_global->msgmap, mn_raw2m(ps_global->msgmap, i));
  3302.  
  3303.     mn_give(&sortmap);
  3304. }
  3305.  
  3306.  
  3307. /*----------------------------------------------------------------------
  3308.     Map sort types to names
  3309.   ----*/
  3310. char *    
  3311. sort_name(so)
  3312.   SortOrder so;
  3313. {
  3314.     /*
  3315.      * Make sure the first upper case letter of any new sort name is
  3316.      * unique.  The command char and label for sort selection is 
  3317.      * derived from this name and its first upper case character.
  3318.      * See mailcmd.c:select_sort().
  3319.      */
  3320.     return((so == SortArrival)  ? "Arrival" :
  3321.         (so == SortDate)     ? "Date" :
  3322.          (so == SortSubject)  ? "Subject" :
  3323.           (so == SortCc)       ? "Cc" :
  3324.            (so == SortFrom)        ? "From" :
  3325.         (so == SortTo)         ? "To" :
  3326.          (so == SortSize)     ? "siZe" :
  3327.           (so == SortSubject2) ? "OrderedSubj" :
  3328.                       "BOTCH");
  3329. }
  3330.  
  3331.  
  3332.  
  3333. /*----------------------------------------------------------------------
  3334.     initialize the subject cache
  3335.   ----*/
  3336. void
  3337. init_subject_cache(n)
  3338.     long n;
  3339. {
  3340.     scache = (struct scache *) fs_get(sizeof(struct scache));
  3341.     memset((void *)scache, 0, sizeof(struct scache));
  3342.     scache->size = n;
  3343. #if    defined(DOS) && !defined(_WINDOWS)
  3344.     scache->cname = temp_nam(NULL, "sc");
  3345.     scache->cfile = fopen(scache->cname, "wb+");
  3346.     /* BUG? go into non-caching mode if failure below? */
  3347.     while(n-- >= 0L){
  3348.     if(fwrite(&scache->ent[1], sizeof(scache_ent), (size_t)1,
  3349.           scache->cfile) != 1){
  3350.         sprintf(tmp_20k_buf, "subject cache: %s",
  3351.             error_description(errno));
  3352.         fatal(tmp_20k_buf);
  3353.     }
  3354.     }
  3355. #else
  3356.     scache->ent  = (char **)fs_get((size_t)(n+1L) * sizeof(char *));
  3357.     memset((void *)scache->ent, 0, (size_t)(n+1L) * sizeof(char *));
  3358. #endif
  3359. }
  3360.  
  3361.  
  3362. /*----------------------------------------------------------------------
  3363.     return the subject of the given number if it's in the cache, else NULL
  3364.   ----*/
  3365. char *
  3366. subject_cache_ent(n)
  3367.     long n;
  3368. {
  3369. #if    defined(DOS) && !defined(_WINDOWS)
  3370.     char *buf;
  3371.     int   i;
  3372.  
  3373.     /* if the one we want's in core, return it */
  3374.     buf = (scache->msgno[i=0] == n || scache->msgno[i=1] == n)
  3375.         ? scache->ent[i].buf : NULL;
  3376.  
  3377.     /* else pick one of the slots, and fetch n's subject off disk */
  3378.     if(!buf){
  3379.     i = subject_cache_slot(n);
  3380.     buf = scache->ent[i].used ? scache->ent[i].buf : NULL;
  3381.     }
  3382.  
  3383.     scache->last = i;
  3384.     return(buf);
  3385. #else
  3386.     return(scache->ent[n]);
  3387. #endif
  3388. }
  3389.  
  3390.  
  3391. /*----------------------------------------------------------------------
  3392.     add the given subject to the cache
  3393.   ----*/
  3394. char *
  3395. subject_cache_add(n, s)
  3396.     long n;
  3397.     char *s;
  3398. {
  3399. #if    defined(DOS) && !defined(_WINDOWS)
  3400.     int i;
  3401.     
  3402.     if(scache->msgno[i=0] != n && scache->msgno[i=1] != n)
  3403.       i = subject_cache_slot(n);
  3404.  
  3405.     scache->last = i;
  3406.     scache->ent[i].used = 1;
  3407.     scache->msgno[i] = n;
  3408.     strncpy(scache->ent[i].buf, s, SUB_CACHE_LEN-1);
  3409.     scache->ent[i].buf[SUB_CACHE_LEN-1] = '\0';
  3410.     return(scache->ent[i].buf);
  3411. #else
  3412.     return(scache->ent[n] = cpystr(s));
  3413. #endif
  3414. }
  3415.  
  3416.  
  3417. /*----------------------------------------------------------------------
  3418.     pick the next slot, and fetch n's subject off disk
  3419.   ----*/
  3420. int
  3421. subject_cache_slot(n)
  3422.     long n;
  3423. {
  3424. #if    defined(DOS) && !defined(_WINDOWS)
  3425.     int i;
  3426.  
  3427.     i = scache->last ? 0 : 1;
  3428.     if(scache->msgno[i]){
  3429.     fseek(scache->cfile, scache->msgno[i] * sizeof(scache_ent), 0);
  3430.     fwrite(&scache->ent[i], sizeof(scache_ent), (size_t)1,
  3431.            scache->cfile);
  3432.     }
  3433.  
  3434.     scache->msgno[i] = n;
  3435.     if(fseek(scache->cfile, scache->msgno[i] * sizeof(scache_ent), 0)
  3436.        || fread(&scache->ent[i], sizeof(scache_ent), (size_t)1,
  3437.         scache->cfile) != 1){
  3438.     sprintf(tmp_20k_buf, "Can't access subject cache: ",
  3439.         error_description(errno));
  3440.     fatal(tmp_20k_buf);
  3441.     }
  3442.  
  3443.     return(i);
  3444. #endif
  3445. }
  3446.  
  3447.  
  3448. /*----------------------------------------------------------------------
  3449.     flush the subject cache
  3450.   ----*/
  3451. void
  3452. clear_subject_cache()
  3453. {
  3454.     if(scache){
  3455. #if    defined(DOS) && !defined(_WINDOWS)
  3456.     fclose(scache->cfile);
  3457.     unlink(scache->cname);
  3458.     fs_give((void **)&scache->cname);
  3459. #else
  3460.     for(; scache->size; scache->size--)
  3461.       if(scache->ent[scache->size])
  3462.         fs_give((void **)&scache->ent[scache->size]);
  3463.  
  3464.     if(scache->ent)
  3465.       fs_give((void **)&scache->ent);
  3466.  
  3467. #endif
  3468.     fs_give((void **)&scache);
  3469.     }
  3470. }
  3471.  
  3472.  
  3473.  
  3474. /*
  3475.  *           * * *  Message number management functions  * * *
  3476.  */
  3477.  
  3478.  
  3479. /*----------------------------------------------------------------------
  3480.   Initialize a message manipulation structure for the given total
  3481.  
  3482.    Accepts: msgs - pointer to message manipulation struct
  3483.         n - number to test
  3484.    Returns: true if n is in selected array, false otherwise
  3485.  
  3486.   ----*/
  3487. void
  3488. msgno_init(msgs, tot)
  3489.      MSGNO_S **msgs;
  3490.      long      tot;
  3491. {
  3492.     long   slop = (tot + 1L) % 64;
  3493.     size_t len;
  3494.  
  3495.     if(!msgs)
  3496.       return;
  3497.  
  3498.     if(!(*msgs)){
  3499.     (*msgs) = (MSGNO_S *)fs_get(sizeof(MSGNO_S));
  3500.     memset((void *)(*msgs), 0, sizeof(MSGNO_S));
  3501.     }
  3502.  
  3503.     (*msgs)->sel_cur  = 0L;
  3504.     (*msgs)->sel_cnt  = 1L;
  3505.     (*msgs)->sel_size = 8L;
  3506.     len              = (size_t)(*msgs)->sel_size * sizeof(long);
  3507.     if((*msgs)->select)
  3508.       fs_resize((void **)&((*msgs)->select), len);
  3509.     else
  3510.       (*msgs)->select = (long *)fs_get(len);
  3511.  
  3512.     (*msgs)->select[0] = (tot) ? 1L : 0L;
  3513.  
  3514.     (*msgs)->sort_size = (tot + 1L) + (64 - slop);
  3515.     len               = (size_t)(*msgs)->sort_size * sizeof(long);
  3516.     if((*msgs)->sort)
  3517.       fs_resize((void **)&((*msgs)->sort), len);
  3518.     else
  3519.       (*msgs)->sort = (long *)fs_get(len);
  3520.  
  3521.     memset((void *)(*msgs)->sort, 0, len);
  3522.     for(slop = 1L ; slop <= tot; slop++)    /* reusing "slop" */
  3523.       (*msgs)->sort[slop] = slop;
  3524.  
  3525.     (*msgs)->max_msgno    = tot;
  3526.     (*msgs)->sort_order   = ps_global->def_sort;
  3527.     (*msgs)->reverse_sort = ps_global->def_sort_rev;
  3528.     (*msgs)->flagged_hid  = 0L;
  3529.     (*msgs)->flagged_exld = 0L;
  3530.     (*msgs)->flagged_tmp  = 0L;
  3531. }
  3532.  
  3533.  
  3534.  
  3535. /*----------------------------------------------------------------------
  3536.  Increment the current message number
  3537.  
  3538.    Accepts: msgs - pointer to message manipulation struct
  3539.   ----*/
  3540. void
  3541. msgno_inc(stream, msgs)
  3542.      MAILSTREAM *stream;
  3543.      MSGNO_S    *msgs;
  3544. {
  3545.     long i;
  3546.  
  3547.     if(!msgs || mn_get_total(msgs) < 1L)
  3548.       return;
  3549.  
  3550.     for(i = msgs->select[msgs->sel_cur] + 1; i <= mn_get_total(msgs); i++){
  3551.     if(!get_lflag(stream, msgs, i, MN_HIDE)){
  3552.         (msgs)->select[((msgs)->sel_cur)] = i;
  3553.         break;
  3554.     }
  3555.     }
  3556. }
  3557.  
  3558.  
  3559.  
  3560. /*----------------------------------------------------------------------
  3561.   Decrement the current message number
  3562.  
  3563.    Accepts: msgs - pointer to message manipulation struct
  3564.   ----*/
  3565. void
  3566. msgno_dec(stream, msgs)
  3567.      MAILSTREAM *stream;
  3568.      MSGNO_S     *msgs;
  3569. {
  3570.     long i;
  3571.  
  3572.     if(!msgs || mn_get_total(msgs) < 1L)
  3573.       return;
  3574.  
  3575.     for(i = (msgs)->select[((msgs)->sel_cur)] - 1L; i >= 1L; i--){
  3576.     if(!get_lflag(stream, msgs, i, MN_HIDE)){
  3577.         (msgs)->select[((msgs)->sel_cur)] = i;
  3578.         break;
  3579.     }
  3580.     }
  3581. }
  3582.  
  3583.  
  3584.  
  3585. /*----------------------------------------------------------------------
  3586.   Got thru the message mapping table, and remove messages with DELETED flag
  3587.  
  3588.    Accepts: stream -- mail stream to removed message references from
  3589.         msgs -- pointer to message manipulation struct
  3590.         f -- flags to use a purge criteria
  3591.   ----*/
  3592. void
  3593. msgno_exclude(stream, msgs)
  3594.      MAILSTREAM *stream;
  3595.      MSGNO_S     *msgs;
  3596. {
  3597.     long      i, j;
  3598.  
  3599.     if(!msgs || msgs->max_msgno < 1L)
  3600.       return;
  3601.  
  3602.     /*
  3603.      * With 3.91 we're using a new strategy for finding and operating
  3604.      * on all the messages with deleted status.  The idea is to do a
  3605.      * mail_search for deleted messages so the elt's "searched" bit gets
  3606.      * set, and then to scan the elt's for them and set our local bit
  3607.      * to indicate they're excluded...
  3608.      */
  3609.     (void)count_flagged(stream, "DELETED");
  3610.  
  3611.     for(i = 1L; i <= msgs->max_msgno; ){
  3612.     if(mail_elt(stream, mn_m2raw(msgs, i))->searched){
  3613.         /*--- clear all flags to keep our counts consistent  ---*/
  3614.         set_lflag(stream, msgs, i, (MN_HIDE|MN_SLCT), 0);
  3615.         set_lflag(stream, msgs, i, MN_EXLD, 1); /* mark excluded */
  3616.  
  3617.         /* --- erase knowledge in sort array (shift array down) --- */
  3618.         for(j = i + 1; j <= msgs->max_msgno; j++)
  3619.           msgs->sort[j-1] = msgs->sort[j];
  3620. /* BUG: should compress strings of deleted message numbers */
  3621.  
  3622.         msgs->max_msgno = max(0L, msgs->max_msgno - 1L);
  3623.         msgno_flush_selected(msgs, i);
  3624.     }
  3625.     else
  3626.       i++;
  3627.     }
  3628.  
  3629.     /*
  3630.      * If we excluded away a zoomed display, unhide everything...
  3631.      */
  3632.     if(msgs->max_msgno > 0L && any_lflagged(msgs, MN_HIDE) >= msgs->max_msgno)
  3633.       for(i = 1L; i <= msgs->max_msgno; i++)
  3634.     set_lflag(stream, msgs, i, MN_HIDE, 0);
  3635. }
  3636.  
  3637.  
  3638.  
  3639. /*----------------------------------------------------------------------
  3640.   Got thru the message mapping table, and remove messages with given flag
  3641.  
  3642.    Accepts: stream -- mail stream to removed message references from
  3643.         msgs -- pointer to message manipulation struct
  3644.         f -- flags to use a purge criteria
  3645.   ----*/
  3646. void
  3647. msgno_include(stream, msgs)
  3648.      MAILSTREAM *stream;
  3649.      MSGNO_S     *msgs;
  3650. {
  3651.     long   i, slop, old_total, old_size;
  3652.     size_t len;
  3653.  
  3654.     for(i = 1L; i <= stream->nmsgs; i++)
  3655.       if(get_lflag(stream, NULL, i, MN_EXLD)){
  3656.       old_total        = msgs->max_msgno;
  3657.       old_size         = msgs->sort_size;
  3658.       slop             = (msgs->max_msgno + 1L) % 64;
  3659.       msgs->sort_size  = (msgs->max_msgno + 1L) + (64 - slop);
  3660.       len           = (size_t) msgs->sort_size * sizeof(long);
  3661.       if(msgs->sort){
  3662.           if(old_size != msgs->sort_size)
  3663.         fs_resize((void **)&(msgs->sort), len);
  3664.       }
  3665.       else
  3666.         msgs->sort = (long *)fs_get(len);
  3667.  
  3668.       msgs->sort[++msgs->max_msgno] = i;
  3669.       set_lflag(stream, msgs, msgs->max_msgno, MN_EXLD, 0);
  3670.  
  3671.       if(old_total <= 0L){            /* if no previous messages, */
  3672.           if(!msgs->select){        /* select the new message   */
  3673.           msgs->sel_size = 8L;
  3674.           len         = (size_t)msgs->sel_size * sizeof(long);
  3675.           msgs->select   = (long *)fs_get(len);
  3676.           }
  3677.  
  3678.           msgs->sel_cnt   = 1L;
  3679.           msgs->sel_cur   = 0L;
  3680.           msgs->select[0] = 1L;
  3681.       }
  3682.       }
  3683. }
  3684.  
  3685.  
  3686.  
  3687. /*----------------------------------------------------------------------
  3688.  Add the given number of raw message numbers to the end of the
  3689.  current list...
  3690.  
  3691.    Accepts: msgs - pointer to message manipulation struct
  3692.         n - number to add
  3693.    Returns: with fixed up msgno struct
  3694.  
  3695.    Only have to adjust the sort array, as since new mail can't cause
  3696.    selection!
  3697.   ----*/
  3698. void
  3699. msgno_add_raw(msgs, n)
  3700.      MSGNO_S *msgs;
  3701.      long     n;
  3702. {
  3703.     long   slop, old_total, old_size;
  3704.     size_t len;
  3705.  
  3706.     if(!msgs || n <= 0L)
  3707.       return;
  3708.  
  3709.     old_total        = msgs->max_msgno;
  3710.     old_size         = msgs->sort_size;
  3711.     msgs->max_msgno += n;
  3712.     slop             = (msgs->max_msgno + 1L) % 64;
  3713.     msgs->sort_size  = (msgs->max_msgno + 1L) + (64 - slop);
  3714.     len             = (size_t) msgs->sort_size * sizeof(long);
  3715.     if(msgs->sort){
  3716.     if(old_size != msgs->sort_size)
  3717.       fs_resize((void **)&(msgs->sort), len);
  3718.     }
  3719.     else
  3720.       msgs->sort = (long *)fs_get(len);
  3721.  
  3722.     for(slop = old_total + 1L; slop <= msgs->max_msgno; slop++)
  3723.       msgs->sort[slop] = slop;
  3724.  
  3725.     if(old_total <= 0L){            /* if no previous messages, */
  3726.     if(!msgs->select){            /* select the new message   */
  3727.         msgs->sel_size = 8L;
  3728.         len           = (size_t)msgs->sel_size * sizeof(long);
  3729.         msgs->select   = (long *)fs_get(len);
  3730.     }
  3731.  
  3732.     msgs->sel_cnt   = 1L;
  3733.     msgs->sel_cur   = 0L;
  3734.     msgs->select[0] = 1L;
  3735.     }
  3736. }
  3737.  
  3738.  
  3739.  
  3740. /*----------------------------------------------------------------------
  3741.   Remove all knowledge of the given raw message number
  3742.  
  3743.    Accepts: msgs - pointer to message manipulation struct
  3744.         n - number to remove
  3745.    Returns: with fixed up msgno struct
  3746.  
  3747.    After removing *all* references, adjust the sort array and
  3748.    various pointers accordingly...
  3749.   ----*/
  3750. void
  3751. msgno_flush_raw(msgs, n)
  3752.      MSGNO_S *msgs;
  3753.      long     n;
  3754. {
  3755.     long i, old_sorted = 0L;
  3756.     int  shift = 0;
  3757.  
  3758.     if(!msgs)
  3759.       return;
  3760.  
  3761.     /*---- blast n from sort array ----*/
  3762.     for(i = 1L; i <= msgs->max_msgno; i++){
  3763.     if(msgs->sort[i] == n){
  3764.         old_sorted = i;
  3765.         shift++;
  3766.     }
  3767.  
  3768.     if(shift)
  3769.       msgs->sort[i] = msgs->sort[i + 1L];
  3770.  
  3771.     if(msgs->sort[i] > n)
  3772.       msgs->sort[i] -= 1L;
  3773.     }
  3774.  
  3775.     /*---- now, fixup select array ----*/
  3776.     msgs->max_msgno = max(0L, msgs->max_msgno - 1L);
  3777.     msgno_flush_selected(msgs, old_sorted);
  3778. }
  3779.  
  3780.  
  3781.  
  3782. /*----------------------------------------------------------------------
  3783.   Remove all knowledge of the given selected message number
  3784.  
  3785.    Accepts: msgs - pointer to message manipulation struct
  3786.         n - number to remove
  3787.    Returns: with fixed up selec members in msgno struct
  3788.  
  3789.    Remove reference and fix up selected message numbers beyond
  3790.    the specified number
  3791.   ----*/
  3792. void
  3793. msgno_flush_selected(msgs, n)
  3794.      MSGNO_S *msgs;
  3795.      long     n;
  3796. {
  3797.     long i;
  3798.     int  shift = 0;
  3799.  
  3800.     for(i = 0L; i < msgs->sel_cnt; i++){
  3801.     if(!shift && (msgs->select[i] == n))
  3802.       shift++;
  3803.  
  3804.     if(shift && i + 1L < msgs->sel_cnt)
  3805.       msgs->select[i] = msgs->select[i + 1L];
  3806.  
  3807.     if(n < msgs->select[i] || msgs->select[i] > msgs->max_msgno)
  3808.       msgs->select[i] -= 1L;
  3809.     }
  3810.  
  3811.     if(shift && msgs->sel_cnt > 1L)
  3812.       msgs->sel_cnt -= 1L;
  3813. }
  3814.  
  3815.  
  3816.  
  3817. /*----------------------------------------------------------------------
  3818.   Test to see if the given message number is in the selected message
  3819.   list...
  3820.  
  3821.    Accepts: msgs - pointer to message manipulation struct
  3822.         n - number to test
  3823.    Returns: true if n is in selected array, false otherwise
  3824.  
  3825.   ----*/
  3826. int
  3827. msgno_in_select(msgs, n)
  3828.      MSGNO_S *msgs;
  3829.      long     n;
  3830. {
  3831.     long i;
  3832.  
  3833.     if(msgs)
  3834.       for(i = 0L; i < msgs->sel_cnt; i++)
  3835.     if(msgs->select[i] == n)
  3836.       return(1);
  3837.  
  3838.     return(0);
  3839. }
  3840.  
  3841.  
  3842.  
  3843. /*----------------------------------------------------------------------
  3844.   return our index number for the given raw message number
  3845.  
  3846.    Accepts: msgs - pointer to message manipulation struct
  3847.         n - number to locate
  3848.    Returns: our index number of given raw message
  3849.  
  3850.   ----*/
  3851. long
  3852. msgno_in_sort(msgs, n)
  3853.      MSGNO_S *msgs;
  3854.      long     n;
  3855. {
  3856.     static long start = 1L;
  3857.     long        i;
  3858.  
  3859.     if(mn_get_total(msgs) < 1L)
  3860.       return(-1L);
  3861.     else if(mn_get_sort(msgs) == SortArrival && !any_lflagged(msgs, MN_EXLD))
  3862.       return((mn_get_revsort(msgs)) ? 1 + mn_get_total(msgs) - n  : n);
  3863.  
  3864.     if(start > mn_get_total(msgs))        /* reset start? */
  3865.       start = 1L;
  3866.  
  3867.     i = start;
  3868.     do {
  3869.     if(mn_m2raw(msgs, i) == n)
  3870.       return(start = i);
  3871.  
  3872.     if(++i > mn_get_total(msgs))
  3873.       i = 1L;
  3874.     }
  3875.     while(i != start);
  3876.  
  3877.     return(0L);
  3878. }
  3879.  
  3880.  
  3881.  
  3882. /*
  3883.  *           * * *  Index entry cache manager  * * *
  3884.  */
  3885.  
  3886. /*
  3887.  * at some point, this could be made part of the pine_state struct.
  3888.  * the only changes here would be to pass the ps pointer around
  3889.  */
  3890. static struct index_cache {
  3891.    void      *cache;                /* pointer to cache         */
  3892.    char      *name;                /* pointer to cache name    */
  3893.    long    num;                    /* # of last index in cache */
  3894.    size_t  size;                /* size of each index line  */
  3895.    int     need_format_setup;
  3896. } icache = { (void *) NULL, (char *) NULL, (long) 0, (size_t) 0, (int) 0 };
  3897.   
  3898. /*
  3899.  * cache size growth increment
  3900.  */
  3901.  
  3902. #ifdef    DOS
  3903. /*
  3904.  * the idea is to have the cache increment be a multiple of the block
  3905.  * size (4K), for efficient swapping of blocks.  we can pretty much
  3906.  * assume 81 character lines.
  3907.  *
  3908.  * REMEMBER: number of lines in the incore cache has to be a multiple 
  3909.  *           of the cache growth increment!
  3910.  */
  3911. #define    IC_SIZE        (50L)            /* cache growth increment  */
  3912. #define    ICC_SIZE    (50L)            /* enties in incore cache  */
  3913. #define FUDGE           (46L)            /* extra chars to make 4096*/
  3914.  
  3915. static char    *incore_cache = NULL;        /* pointer to incore cache */
  3916. static long      cache_block_s = 0L;        /* save recomputing time   */
  3917. static long      cache_base = 0L;        /* index of line 0 in block*/
  3918. #else
  3919. #define    IC_SIZE        100
  3920. #endif
  3921.  
  3922. /*
  3923.  * important values for cache building
  3924.  */
  3925. static MAILSTREAM *bc_this_stream = NULL;
  3926. static long  bc_start, bc_current;
  3927. static short bc_done = 0;
  3928.  
  3929.  
  3930. /*
  3931.  * way to return the current cache entry size
  3932.  */
  3933. int
  3934. i_cache_width()
  3935. {
  3936.     return(icache.size - sizeof(HLINE_S));
  3937. }
  3938.  
  3939.  
  3940. /* 
  3941.  * i_cache_size - make sure the cache is big enough to contain
  3942.  * requested entry
  3943.  */
  3944. int
  3945. i_cache_size(indx)
  3946.     long         indx;
  3947. {
  3948.     long j;
  3949.     size_t  newsize = sizeof(HLINE_S)
  3950.              + (max(ps_global->ttyo->screen_cols, 80) * sizeof(char));
  3951.  
  3952.     if(j = (newsize % sizeof(long)))        /* alignment hack */
  3953.       newsize += (sizeof(long) - (size_t)j);
  3954.  
  3955.     if(icache.size != newsize){
  3956.     clear_index_cache();            /* clear cache, start over! */
  3957.     icache.size = newsize;
  3958.     }
  3959.  
  3960.     if(indx > (j = icache.num - 1L)){        /* make room for entry! */
  3961.     size_t  tmplen = icache.size;
  3962.     char   *tmpline;
  3963.  
  3964.     while(indx >= icache.num)
  3965.       icache.num += IC_SIZE;
  3966.  
  3967. #ifdef    DOS
  3968.     tmpline = fs_get(tmplen);
  3969.     memset(tmpline, 0, tmplen);
  3970.     if(icache.cache == NULL){
  3971.         if(!icache.name)
  3972.           icache.name = temp_nam(NULL, "pi");
  3973.  
  3974.         if((icache.cache = (void *)fopen(icache.name,"w+b")) == NULL){
  3975.         sprintf(tmp_20k_buf, "Can't open index cache: %s",icache.name);
  3976.         fatal(tmp_20k_buf);
  3977.         }
  3978.  
  3979.         for(j = 0; j < icache.num; j++){
  3980.             if(fwrite(tmpline,tmplen,(size_t)1,(FILE *)icache.cache) != 1)
  3981.           fatal("Can't write index cache in resize");
  3982.  
  3983.         if(j%ICC_SIZE == 0){
  3984.           if(fwrite(tmpline,(size_t)FUDGE,
  3985.                 (size_t)1,(FILE *)icache.cache) != 1)
  3986.             fatal("Can't write FUDGE factor in resize");
  3987.             }
  3988.         }
  3989.     }
  3990.     else{
  3991.         /* init new entries */
  3992.         fseek((FILE *)icache.cache, 0L, 2);        /* seek to end */
  3993.  
  3994.         for(;j < icache.num; j++){
  3995.             if(fwrite(tmpline,tmplen,(size_t)1,(FILE *)icache.cache) != 1)
  3996.           fatal("Can't write index cache in resize");
  3997.  
  3998.         if(j%ICC_SIZE == 0){
  3999.           if(fwrite(tmpline,(size_t)FUDGE,
  4000.                 (size_t)1,(FILE *)icache.cache) != 1)
  4001.             fatal("Can't write FUDGE factor in resize");
  4002.             }
  4003.         }
  4004.     }
  4005.  
  4006.     fs_give((void **)&tmpline);
  4007. #else
  4008.     if(icache.cache == NULL){
  4009.         icache.cache = (void *)fs_get((icache.num+1)*tmplen);
  4010.         memset(icache.cache, 0, (icache.num+1)*tmplen);
  4011.     }
  4012.     else{
  4013.             fs_resize((void **)&(icache.cache), (size_t)(icache.num+1)*tmplen);
  4014.         tmpline = (char *)icache.cache + ((j+1) * tmplen);
  4015.         memset(tmpline, 0, (icache.num - j) * tmplen);
  4016.     }
  4017. #endif
  4018.     }
  4019.  
  4020.     return(1);
  4021. }
  4022.  
  4023. #ifdef    DOS
  4024. /*
  4025.  * read a block into the incore cache
  4026.  */
  4027. void
  4028. icread()
  4029. {
  4030.     size_t n;
  4031.  
  4032.     if(fseek((FILE *)icache.cache, (cache_base/ICC_SIZE) * cache_block_s, 0))
  4033.       fatal("ran off end of index cache file in icread");
  4034.  
  4035.     n = fread((void *)incore_cache, (size_t)cache_block_s, 
  4036.         (size_t)1, (FILE *)icache.cache);
  4037.  
  4038.     if(n != 1L)
  4039.       fatal("Can't read index cache block in from disk");
  4040. }
  4041.  
  4042.  
  4043. /*
  4044.  * write the incore cache out to disk
  4045.  */
  4046. void
  4047. icwrite()
  4048. {
  4049.     size_t n;
  4050.  
  4051.     if(fseek((FILE *)icache.cache, (cache_base/ICC_SIZE) * cache_block_s, 0))
  4052.       fatal("ran off end of index cache file in icwrite");
  4053.  
  4054.     n = fwrite((void *)incore_cache, (size_t)cache_block_s,
  4055.         (size_t)1, (FILE *)icache.cache);
  4056.  
  4057.     if(n != 1L)
  4058.       fatal("Can't write index cache block in from disk");
  4059. }
  4060.  
  4061.  
  4062. /*
  4063.  * make sure the necessary block of index lines is in core
  4064.  */
  4065. void
  4066. i_cache_hit(indx)
  4067.     long         indx;
  4068. {
  4069.     dprint(9, (debugfile, "i_cache_hit: %ld\n", indx));
  4070.     /* no incore cache, create it */
  4071.     if(!incore_cache){
  4072.     cache_block_s = (((long)icache.size * ICC_SIZE) + FUDGE)*sizeof(char);
  4073.     incore_cache  = (char *)fs_get((size_t)cache_block_s);
  4074.     cache_base = (indx/ICC_SIZE) * ICC_SIZE;
  4075.     icread();
  4076.     return;
  4077.     }
  4078.  
  4079.     if(indx >= cache_base && indx < (cache_base + ICC_SIZE))
  4080.     return;
  4081.  
  4082.     icwrite();
  4083.  
  4084.     cache_base = (indx/ICC_SIZE) * ICC_SIZE;
  4085.     icread();
  4086. }
  4087. #endif
  4088.  
  4089.  
  4090. /*
  4091.  * return the index line associated with the given message number
  4092.  */
  4093. HLINE_S *
  4094. get_index_cache(msgno)
  4095.     long         msgno;
  4096. {
  4097.     if(!i_cache_size(--msgno)){
  4098.     q_status_message(SM_ORDER, 0, 3, "get_index_cache failed!");
  4099.     return(NULL);
  4100.     }
  4101.  
  4102. #ifdef    DOS
  4103.     i_cache_hit(msgno);            /* get entry into core */
  4104.     return((HLINE_S *)(incore_cache 
  4105.           + ((msgno%ICC_SIZE) * (long)max(icache.size,FUDGE))));
  4106. #else
  4107.     return((HLINE_S *) ((char *)(icache.cache) 
  4108.        + (msgno * (long)icache.size * sizeof(char))));
  4109. #endif
  4110. }
  4111.  
  4112.  
  4113. /*
  4114.  * the idea is to pre-build and cache index lines while waiting
  4115.  * for command input.
  4116.  */
  4117. void
  4118. build_header_cache()
  4119. {
  4120.     long lines_per_page = max(0,ps_global->ttyo->screen_rows - 5);
  4121.  
  4122.     if(mn_get_total(ps_global->msgmap) == 0 || ps_global->mail_stream == NULL
  4123.        || (bc_this_stream == ps_global->mail_stream && bc_done >= 2)
  4124.        || any_lflagged(ps_global->msgmap, (MN_HIDE|MN_EXLD|MN_SLCT)))
  4125.       return;
  4126.  
  4127.     if(bc_this_stream != ps_global->mail_stream){ /* reset? */
  4128.     bc_this_stream = ps_global->mail_stream;
  4129.     bc_current = bc_start = top_ent_calc(ps_global->mail_stream,
  4130.                          ps_global->msgmap,
  4131.                          mn_get_cur(ps_global->msgmap),
  4132.                          lines_per_page);
  4133.     bc_done  = 0;
  4134.     }
  4135.  
  4136.     if(!bc_done && bc_current > mn_get_total(ps_global->msgmap)){ /* wrap? */
  4137.     bc_current = bc_start - lines_per_page;
  4138.     bc_done++;
  4139.     }
  4140.     else if(bc_done == 1 && (bc_current % lines_per_page) == 1)
  4141.       bc_current -= (2L * lines_per_page);
  4142.  
  4143.     if(bc_current < 1)
  4144.       bc_done = 2;            /* really done! */
  4145.     else
  4146.       (void)build_header_line(ps_global, ps_global->mail_stream,
  4147.                   ps_global->msgmap, bc_current++);
  4148. }
  4149.  
  4150.  
  4151. /*
  4152.  * erase a particular entry in the cache
  4153.  */
  4154. void
  4155. clear_index_cache_ent(indx)
  4156.     long indx;
  4157. {
  4158.     HLINE_S *tmp = get_index_cache(indx);
  4159.  
  4160.     tmp->line[0] = '\0';
  4161.     tmp->id      = 0L;
  4162. }
  4163.  
  4164.  
  4165. /*
  4166.  * clear the index cache associated with the current mailbox
  4167.  */
  4168. void
  4169. clear_index_cache()
  4170. {
  4171. #ifdef    DOS
  4172.     cache_base = 0L;
  4173.     if(incore_cache)
  4174.       fs_give((void **)&incore_cache);
  4175.  
  4176.     if(icache.cache){
  4177.     fclose((FILE *)icache.cache);
  4178.     icache.cache = NULL;
  4179.     }
  4180.  
  4181.     if(icache.name){
  4182.     unlink(icache.name);
  4183.     fs_give((void **)&icache.name);
  4184.     }
  4185. #else
  4186.     if(icache.cache)
  4187.       fs_give((void **)&(icache.cache));
  4188. #endif
  4189.     icache.num  = 0L;
  4190.     icache.size = 0;
  4191.     bc_this_stream = NULL;
  4192.     set_need_format_setup();
  4193. }
  4194.  
  4195.  
  4196. void
  4197. set_need_format_setup()
  4198. {
  4199.     icache.need_format_setup = 1;
  4200. }
  4201.  
  4202.  
  4203. void
  4204. clear_need_format_setup()
  4205. {
  4206.     icache.need_format_setup = 0;
  4207. }
  4208.  
  4209.  
  4210. int
  4211. check_need_format_setup()
  4212. {
  4213.     return(icache.need_format_setup);
  4214. }
  4215.  
  4216.  
  4217. #ifdef    DOS
  4218. /*
  4219.  * flush the incore_cache, but not the whole enchilada
  4220.  */
  4221. void
  4222. flush_index_cache()
  4223. {
  4224.     if(incore_cache){
  4225.     if(mn_get_total(ps_global->msgmap) > 0L)
  4226.       icwrite();            /* write this block out to disk */
  4227.  
  4228.     fs_give((void **)&incore_cache);
  4229.     cache_base = 0L;
  4230.     }
  4231. }
  4232. #endif
  4233.  
  4234.  
  4235. #ifdef _WINDOWS
  4236. /*----------------------------------------------------------------------
  4237.   Callback to get the text of the current message.  Used to display
  4238.   a message in an alternate window.      
  4239.  
  4240.   Args: cmd - what type of scroll operation.
  4241.     text - filled with pointer to text.
  4242.     l - length of text.
  4243.     style - Returns style of text.  Can be:
  4244.         GETTEXT_TEXT - Is a pointer to text with CRLF deliminated
  4245.                 lines
  4246.         GETTEXT_LINES - Is a pointer to NULL terminated array of
  4247.                 char *.  Each entry points to a line of
  4248.                 text.
  4249.                     
  4250.         this implementation always returns GETTEXT_TEXT.
  4251.  
  4252.   Returns: TRUE - did the scroll operation.
  4253.        FALSE - was not able to do the scroll operation.
  4254.  ----*/
  4255. int
  4256. index_scroll_callback (cmd, scroll_pos)
  4257. int    cmd;
  4258. long    scroll_pos;
  4259. {
  4260.     int paint = TRUE;
  4261.     
  4262.     switch (cmd) {
  4263.       case MSWIN_KEY_SCROLLUPLINE:
  4264.     paint = index_scroll_up (1);
  4265.     break;
  4266.  
  4267.       case MSWIN_KEY_SCROLLDOWNLINE:
  4268.     paint = index_scroll_down (1);
  4269.     break;
  4270.  
  4271.       case MSWIN_KEY_SCROLLUPPAGE:
  4272.     paint = index_scroll_up (current_index_state->lines_per_page);
  4273.     break;
  4274.  
  4275.       case MSWIN_KEY_SCROLLDOWNPAGE:
  4276.     paint = index_scroll_down (current_index_state->lines_per_page);
  4277.     break;
  4278.  
  4279.       case MSWIN_KEY_SCROLLTO:
  4280.     paint = index_scroll_to_pos (scroll_pos + 1);
  4281.     break;
  4282.     }
  4283.  
  4284.     if(paint){
  4285.     mswin_beginupdate();
  4286.     update_titlebar_message();
  4287.     update_titlebar_status();
  4288.     redraw_index_body();
  4289.     mswin_endupdate();
  4290.     }
  4291.  
  4292.     return(paint);
  4293. }
  4294.  
  4295.  
  4296. /*----------------------------------------------------------------------
  4297.      MSWin scroll callback to get the text of the current message
  4298.  
  4299.   Args: title - title for new window
  4300.     text - 
  4301.     l - 
  4302.     style - 
  4303.  
  4304.   Returns: TRUE - got the requested text
  4305.        FALSE - was not able to get the requested text
  4306.  ----*/
  4307. int
  4308. index_gettext_callback(title, text, l, style)
  4309.     char  *title;
  4310.     void **text;
  4311.     long  *l;
  4312.     int   *style;
  4313. {
  4314.     ENVELOPE *env;
  4315.     BODY     *body;
  4316.     STORE_S  *so;
  4317.     gf_io_t   pc;
  4318.  
  4319.     if(mn_get_total(ps_global->msgmap) > 0L
  4320.        && (so = so_get(CharStar, NULL, WRITE_ACCESS))){
  4321.     gf_set_so_writec(&pc, so);
  4322.  
  4323.     if((env = mail_fetchstructure(ps_global->mail_stream,
  4324.                       mn_m2raw(ps_global->msgmap,
  4325.                            mn_get_cur(ps_global->msgmap)),
  4326.                       &body))
  4327.        && format_message(mn_m2raw(ps_global->msgmap,
  4328.                       mn_get_cur(ps_global->msgmap)),
  4329.                  env, body, FM_NEW_MESS, pc)){
  4330.         sprintf(title, "Folder %.50s  --  Message %ld of %ld",
  4331.             ps_global->cur_folder,
  4332.             mn_get_cur(ps_global->msgmap),
  4333.             mn_get_total(ps_global->msgmap));
  4334.         *text  = so_text(so);
  4335.         *l     = strlen((char *)so_text(so));
  4336.         *style = GETTEXT_TEXT;
  4337.  
  4338.         /* free alloc'd so, but preserve the text passed back to caller */
  4339.         so->txt = (void *)NULL;
  4340.         so_give(&so);
  4341.         return(1);
  4342.     }
  4343.     }
  4344.  
  4345.     return(0);
  4346. }
  4347. #endif    /* _WINDOWS */
  4348.